:dev profile :main making REPL blow up


#1

In the webdev tutorial, after adding the :dev profile, I noticed that lein repl started breaking:

#error {
 :cause Could not locate _dev_main__init.class or _dev_main.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
 :via
 [{:type java.io.FileNotFoundException
   :message Could not locate _dev_main__init.class or _dev_main.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
   :at [clojure.lang.RT load RT.java 449]}]

I take it that Clojure is trying to load the :dev profile’s :main as a namespace, hence the complaints about not finding the .clj file:

:profiles {:dev
             {:main webdev.core/-dev-main}})

My project for the tutorial is here: https://github.com/brentvukmer/webdev-clojure

On a more meta level, as a Clojure newbie should I note these kinds of errors and move on (since lein run still works) or try to dig in and debug the problem? My gut feeling is that I should keep working through the tutorials and then come back and dig into my backlog of questions.


#2

Hi Brent,

Thanks for the question and sorry for the trouble.

It appears that that’s a bug with Leiningen. Leiningen definitely allows you to specify a main namespace and function for lein run. However, I can’t find it anywhere in the documentation. It’s likely that the bug is that it allows lein run with that :main directive at all.

I’ll have to fix this in the course. I never tested running lein repl after putting that in.

The correct way to do a similar thing is to make a new namespace just for dev and put a -main function in it (the current -dev-main). It appears that all main functions should be called -main.

I would ask them here and move on if you can. No need to waste time on tooling when you’re first learning.

Eric


#3

Thanks for the feedback @ericnormand


#4

Hi there,

I’ve updated the Web Dev course Part 1. I’ve gone through and made a git repo with the code from the video. One of the changes I made was to fix this very issue. You can see how I solved it here:

and

Rock on!
Eric


#5

Thanks for working on this, @ericnormand. I will check it out!


#6

Eric,

I need a bit of clarification. What is intended to be included in the webdev.dev file/namespace? I see dependencies but not the body. Does the body of the application go in webdev.dev, webdev.core, or both? What goes where and why? Is this just pointing to webdev.core and I write the logic there? What is this redirecting doing and how do I think about it?

A beginner thanks you ahead of time.


#7

Hey there @IntegralExplorer,

Great question.

The webdev.dev namespace is there for starting the app in a slightly different way for development purposes. The -main function defined in there uses a middleware called wrap-reload to automatically reload the code that changes between requests. It lets you edit your code, save it, and reload the browser to see changes–all without restarting the server.

I’m not sure why you’re saying there’s no body. It’s there as far as I can see. Just for reference, here’s the file it’s defined in:
dev/webdev/dev.clj.

Rock on!
Eric


#8

Hi Eric,

Thanks for the reply. Let me clarify a bit.

When I say “body”, I meant to say the “code that is being changed” say in webdev.core and any other namespaces that are pointing to one another. I can see the confusion in my original question. All the text in the file is the “body”. But here are some things I am trying to understand:

Assertions:

  1. webdev.dev points to both webdev.item.model and ring.adapter.jetty
  2. webdev.core also points to webdev.item.model and ring.adapter.jetty - and many other required libraries
  3. webdev.dev also points to webdev.core - which I assume is pointing to the application’s “body” of code
  4. webdev.dev points to ring.middleware.reload to enable it to reload the changed code without restart

Question: Why is it necessary to duplicate the :requires that are in webdev.core here in webdev.dev? It seems that pointing to webdev.core would also :require them.

Question: What are the rules-of-thumb in deciding what to put where in this context?

I made the mistake in my coding of not putting “core” in front of "db: as in core/db when creating this file. Can you confirm my new understanding: I was pointing to nothing since the “create-table” function was not defined in this webdev.dev file. When I add the “core/”, I am saying which namespace the function is defined. So, I needed to point to the webdev.core file where the “create-table” function was defined. Is this what is happening?

For reference:

 (ns webdev.dev
  (:require [webdev.item.model :as items])
  (:require [ring.adapter.jetty :as jetty]
            [ring.middleware.reload :refer [wrap-reload]]
            [webdev.core :as core]))

(defn -main [port]
  (items/create-table core/db)
  (jetty/run-jetty (wrap-reload #'core/app) {:port (Integer. port)}))

#9

Hey @IntegralExplorer,

Sorry I missed this message! It’s been too long! Thanks for the question.

The :require statements in the ns declaration are doing two things. One, they’re saying “load this if it’s not already loaded”. So those are probably already loaded, especially on the reload. Two, they’re defining aliases, which is what the :as directive is saying. The alias lets you refer to the namespace with a shorter name.

In the above code with that ns declaration, core/db is equivalent to webdev.core/db. It’s referring to the db Var in the webdev.core namespace.

Those aliases are only valid in the current namespace. Technically, you can refer to any public Var in any namespace–as long as it’s loaded–even without a :require. For instance, I often call clojure.string/join without requiring the namespace, because it’s usually already required. This isn’t good practice :slight_smile: You never know whether something is required or not, and it really just depends on the order things get loaded. It’s better to :require the namespaces your namespace uses.

That’s why you duplicate the :requires. For the aliases (valid only for current namespace) and for modularity (namespace A shouldn’t have to know what namespace B requires).

The rules of thumb are simple: always require anything code in this namespace needs and nothing more. You’ll notice that the code above refers to code in each of the 4 required namespaces.

Rock on!
Eric