Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
508 views
in Technique[技术] by (71.8m points)

clojure - Compojure development without web server restarts

I've written a small Swing App before in Clojure and now I'd like to create an Ajax-style Web-App. Compojure looks like the best choice right now, so that's what I'm going to try out.

I'd like to have a real tiny edit/try feedback-loop, so I'd prefer not to restart the web server after each small change I do.

What's the best way to accomplish this? By default my Compojure setup (the standard stuff with ant deps/ant with Jetty) doesn't seem to reload any changes I do. I'll have to restart with run-server to see the changes. Because of the Java-heritage and the way the system is started etc. This is probably perfectly normal and the way it should be when I start the system from command-line.

Still, there must be a way to reload stuff dynamically while the server is running. Should I use Compojure from REPL to accomplish my goal? If I should, how do I reload my stuff there?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is quite an old question, and there have been some recent changes that make this much easier.

There are two main things that you want:

  1. Control should return to the REPL so you can keep interacting with your server. This is accomplished by adding {:join? false} to options when starting the Jetty server.
  2. You'd like to automatically pick up changes in certain namespaces when the files change. This can be done with Ring's "wrap-reload" middleware.

A toy application would look like this:

(ns demo.core
  (:use webui.nav
    [clojure.java.io]
    [compojure core response]
    [ring.adapter.jetty :only [run-jetty]]
    [ring.util.response]
    [ring.middleware file file-info stacktrace reload])
  (:require [compojure.route :as route] view)
  (:gen-class))

; Some stuff using Fleet omitted.    

(defroutes main-routes
  (GET "/" [] (view/layout {:body (index-page)})
  (route/not-found (file "public/404.html"))
)

(defn app
  []
  (-> main-routes
      (wrap-reload '(demo.core view))
      (wrap-file "public")
      (wrap-file-info)
      (wrap-stacktrace)))

(defn start-server
  []
  (run-jetty (app) {:port 8080 :join? false}))

(defn -main [& args]
  (start-server))

The wrap-reload function decorates your app routes with a function that detects changes in the listed namespaces. When processing a request, if those namespaces have changed on disk, they are reloaded before further request processing. (My "view" namespace is dynamically created by Fleet, so this auto-reloads my templates whenever they change, too.)

I added a few other pieces of middleware that I've found consistently useful. wrap-file handles static assets. wrap-file-info sets the MIME type on those static assets. wrap-stacktrace helps in debugging.

From the REPL, you could start this app by using the namespace and calling start-server directly. The :gen-class keyword and -main function mean that the app can also be packaged as an uberjar for startup from outside the REPL, too. (There's a world outside the REPL? Well, some people have asked for it anyway...)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...