4
votes

I am new to Clojure coding and went through a number of articles to set up Emacs for development.
The setup itself works as designed, but I have been running server from Cider session and killing Emacs entirely when making any code change. Or, I need to find the server process from shell session, and kill it from there. This is far from ideal.

clojure-getting-started/web.clj

(defn -main [& [port]]
  (let [port (Integer. (or port (env :port) 5000))]
    (jetty/run-jetty (site #'app) {:port port :join? false})))

Cider session

clojure-getting-started.web> (defonce server (-main))

After starting the server, I will get the below error with (server) function:

1. Unhandled java.lang.ClassCastException
   org.eclipse.jetty.server.Server cannot be cast to clojure.lang.IFn


The error message makes sense, but how can I refresh my code base using just Emacs?
I suppose (and hope) there is a better way than starting shell session on Emacs and kill the process there...

3
I suggest you read this github.com/stuartsierra/component and watch this video youtube.com/watch?v=13cmHf_kt-Q to get general idea on how to start, stop, reload code, and start web server - mavbozo
Did you look at github.com/clojure-emacs/cider#basic-usage and at the keyboard shortcuts listed there? You'll probably just want to re-evaluate the buffer (C-c C-k) after a change (or use C-x C-e for a single form). If you really need to restart Clojure, you can stop it (C-c C-q) and restart from within Emacs (M-x cider-jack-in). Also server is a simple var, not a function, so just server will return the server object (it got started via -main when you evaluated (defonce server ...). Finally, you want to look at the lein-ring plugin which will simplify your workflow. - schaueho
If you are new to Clojure take a look this chapter on the ebook Clojure for the Brave and True: braveclojure.com/basic-emacs for a step by step easy guide for installing Emacs and Cider. - Rodrigo Taboada

3 Answers

3
votes

Assuming what you are wanting is to run a jetty server and have it load or reload your code when you change it from within emacs. While the advice already given is good, it may be more complex than you need when getting started. My advice is to take advantage of some of the templates out there for lein which will setup a default environment and workflow for you to begin with. You can then refine this default as you learn more until you get the workflow which suits you. My recommendation would be to start witht the default compojure template i.e.

lein new compojure my-project

This creates a bare bones project with the basic ring and compojure libraries and lein plugins as well as a simple dev profile.

Edit the src/my_project/handler.clj file and add the ring.middleware.reload middleware e.g.

(ns my-project.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.reload :refer [wrap-reload]]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

(defroutes app-routes
  (GET "/" [] "Hello World")
  (route/not-found "Not Found"))

(def app
  (-> app-routes
      wrap-reload
      (wrap-defaults site-defaults)))

The wrap-reload middleware will cause your code to be reloaded when it is modified. You won't need to restart the jetty server for your code changes to take effect - just reload the page.

In a terminal run either

lein ring server

or

lein ring server-headless

This will start a jetty server listening on port 3000. Then from within emacs, you can just open a cider repl to use while writing your code. You won't need to restart the server process unless you make changes to your project.clj file. Same with the cider process.

Then, once your comfortable with this, look at the lein-ring documentation. There you will find information on how to setup a repl.clj file within the project. Once you do that, you will be able to do something like

lein repl

and then from within that repl, do something like

(start-server)

which will start the server. You can then switch to emacs and instead of running cider-jack-in, you can do a cider-connect, which will connect tot he already running repl rather than starting a second repl session. Later, if you decide to start also looking at clojurescript, you can look at some of the default templates for clojure+clojurescript apps. I quite like figwheel and use reagent a fair bit, so I also find the reagent template quite good.

There are quite a few lein templates out there and I find it really useful to just run them and have a look at what they do. I then tend to cherry pick the features/options I like.

1
votes

Here's most of the answer I just gave to a similar question:


Try the (refresh) function in the clojure.tools.namespace.repl namespace:

The refresh function will scan all the directories on the classpath for Clojure source files, read their ns declarations, build a graph of their dependencies, and load them in dependency order.

https://github.com/clojure/tools.namespace#reloading-code-usage

We generally add that plus a few other useful things to the user namespace, so it's loaded into the REPL on startup:

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]
            [clojure.repl :refer [doc source]]
            [clojure.pprint :refer [pprint pp]]
            [midje.repl :as midje]
            [clojure.stacktrace :as st]))

To keep that code separate from your main and test sources, put that in a file at <project root>/dev/user.clj, then add the following to your lein project.clj file:

:profiles {:dev {:source-paths ["dev"]}}
0
votes

Concerning the ClassCastException - server needs to be a function of 1 param:

(defonce server (fn [request] (-main)))