4

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

ntalbs
  • 28,700
  • 8
  • 66
  • 83
ryzcode
  • 49
  • 2
  • I suggest you read this https://github.com/stuartsierra/component and watch this video https://www.youtube.com/watch?v=13cmHf_kt-Q to get general idea on how to start, stop, reload code, and start web server – mavbozo Feb 05 '15 at 16:35
  • Did you look at https://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 Feb 05 '15 at 16:56
  • If you are new to Clojure take a look this chapter on the ebook _Clojure for the Brave and True_: http://www.braveclojure.com/basic-emacs/ for a step by step easy guide for installing Emacs and Cider. – Rodrigo Taboada Feb 05 '15 at 21:08

3 Answers3

3

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.

Tim X
  • 4,158
  • 1
  • 20
  • 26
  • The compojure template for leiningen adds `ring/ring-defaults` as a namespace require. The `ring-defaults` includes a reload function (I am assuming its wrap-reload). So there is no need to specifically include a require directive. – practicalli-john Aug 14 '16 at 13:40
  • There is no need to include the `wrap-reload` function in the definition of the app, ie. in `(def app ,,,)`. The `wrap-defaults` middleware contains a function to reload changes upon saving the .clj file. Any changes made to your code that are called from `app-routes` or functions it calls (and so on) is injected into the running REPL. Any changes outside this function chain, especially adding libraries, will require a server restart. – practicalli-john Aug 14 '16 at 13:46
1

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"]}}
Community
  • 1
  • 1
Martin Dow
  • 5,273
  • 4
  • 30
  • 43
0

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

(defonce server (fn [request] (-main)))
Bost Bost
  • 21
  • 4