没有Web服务器重启的Compojure开发

我之前在Clojure编写了一个小的Swing应用程序,现在我想创build一个Ajax风格的Web应用程序。 Compojure现在看起来是最好的select,所以这就是我将要尝试的。

我想要一个真正的微小的编辑/尝试反馈循环,所以我不希望在每次小改动后重新启动Web服务器。

什么是完成这个最好的方法? 默认情况下,我的Compojure设置(标准的东西与antdetes / Ant与Jetty)似乎并没有重新加载我所做的任何更改。 我将不得不重新启动运行服务器来查看更改。 由于Java的遗产和系统启动的方式等。这可能是非常正常的,当我从命令行启动系统应该是这样。

不过,在服务器运行时,必须有一种方式来dynamic地重新加载东西。 我应该使用REPL的Compojure来实现我的目标吗? 如果我应该,我该如何重装我的东西呢?

这是一个相当古老的问题,最近有一些变化使得这一点变得更容易。

有两个主要的东西,你想要的:

  1. 控制应该返回到REPL,以便您可以继续与您的服务器进行交互。 这是通过添加{:join? 假启动Jetty服务器的选项。
  2. 您想在文件更改时自动select某些名称空间中的更改。 这可以通过Ring的“wrap-reload”中间件完成。

玩具应用程序将如下所示:

(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)) 

wrap-reload函数使用检测列出的名称空间中的更改的函数来装饰您的应用程序路由。 处理请求时,如果这些名称空间在磁盘上发生了更改,则会在进一步处理请求之前重新加载它们。 (我的“视图”命名空间是由Fleetdynamic创build的,所以这个自动重新加载我的模板,每当他们改变。

我添加了一些我发现一直有用的其他中间件。 包装文件处理静态资产。 wrap-file-info在这些静态资产上设置MIMEtypes。 wrap-stacktrace有助于debugging。

从REPL开始,您可以使用命名空间并直接调用start-server来启动这个应用程序。 :gen-class关键字和-main函数意味着应用程序也可以从REPL外部打包为一个uberjar用于启动。 (REPL之外还有一个世界呢?有些人反正要求了…)

这是我从Compojure Google Group的James Reeves那里得到的答案(答案在他的许可下):

您可以使用use或require命令中的reload键在Clojure中重新加载命名空间。 例如,假设您有一个包含路由的文件“demo.clj”:

 (ns demo (:use compojure)) (defroutes demo-routes (GET "/" "Hello World") (ANY "*" [404 "Page not found"])) 

在REPL,你可以使用这个文件并启动一个服务器:

 user=> (use 'demo) nil user=> (use 'compojure) nil user=> (run-server {:port 8080} "/*" (servlet demo-routes)) ... 

你也可以把run-server命令放在另一个clojure文件中。 但是,您不希望将其与要重新加载的内容放在同一个文件中。

现在对demo.clj做一些更改。 在REPLtypes中:

 user=> (use 'demo :reload) nil 

现在您的更改应该显示在http:// localhost:8080上

我想补充一个答案,因为自从最新的答案之后,事情已经发生了一些变化,我花了一些时间来寻找这个问题。

  1. 安装leiningen (只需按照说明)

  2. 创build项目

     lein new compojure compojure-test 
  3. 编辑project.clj的环节

     :ring {:handler compojure-test.handler/app :auto-reload? true :auto-refresh? true} 
  4. 在任何你想要的端口上启动服务器

     lein ring server-headless 8080 
  5. 检查服务器是否在浏览器中运行,默认的基本路线应该只是说“Hello world”。 接下来,去修改你的处理程序(它在src / project_name中)。 更改hello世界文本,保存文件并在浏览器中重新加载页面。 它应该反映新的文本。

继Timothy和Jim Downing的链接之后,我最近发布了一个关键的补充,那就是我发现在开发过程中需要自动重新部署compojure应用程序 。

我有一个如下所示的shell脚本:

 #!/bin/sh CLASSPATH=/home/me/install/compojure/compojure.jar CLASSPATH=$CLASSPATH:/home/me/clojure/clojure.jar CLASSPATH=$CLASSPATH:/home/me/clojure-contrib/clojure-contrib.jar CLASSPATH=$CLASSPATH:/home/me/elisp/clojure/swank-clojure for f in /home/me/install/compojure/deps/*.jar; do CLASSPATH=$CLASSPATH:$f done java -server -cp $CLASSPATH clojure.lang.Repl /home/me/code/web/web.clj 

web.clj看起来像这样

 (use '[swank.swank]) (swank.swank/ignore-protocol-version "2009-03-09") (start-server ".slime-socket" :port 4005 :encoding "utf-8") 

每当我想更新服务器,我创build一个从本地机器到远程机器的SSH隧道。

Enclojure和Emacs(运行SLIME + swank-clojure)可以连接到远程的REPL。

这是非常依赖configuration,但适用于我,我认为你可以适应它:

  1. 把compojure.jar和compojure / deps目录下的jar放到你的类path中。 我使用clojure-contrib / launchers / bash / clj-env-dir来做到这一点,你只需要在CLOJURE_EXT中设置目录,就可以findjar。 CLOJURE_EXT冒号分隔的目录path列表,其顶层内容是(直接或作为符号链接)jar文件和/或path将在Clojure的类path中的目录。

  2. 启动clojure REPL

  3. 从compojure根目录粘贴hello.clj示例

  4. 检查本地主机:8080

  5. 重新定义欢迎(defroutes迎宾(GET“/”(html [:h1“Goodbye World”])))

  6. 检查本地主机:8080

也有将REPL附加到现有stream程的方法,或者可以将套接字REPL保留在您的服务器中,或者甚至可以定义一个POST调用,以便在浏览器中重新定义函数。 有很多方法可以解决这个问题。

我想跟进mtnygard的答案,并发布完整的project.clj文件和core.clj文件,得到了给定的function工作。 做了一些修改,这是更多的准系统

预设置命令

 lein new app test-web cd test-web mkdir resources 

project.clj

 (defproject test-web "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [compojure "1.1.6"] [ring "1.2.1"]] :main ^:skip-aot test-web.core :target-path "target/%s" :profiles {:uberjar {:aot :all}}) 

core.clj

 (ns test-web.core (:use [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]) (:gen-class)) (defroutes main-routes (GET "/" [] "Hello World!!") (GET "/hello" [] (hello)) (route/not-found "NOT FOUND")) (def app (-> main-routes (wrap-reload '(test-web.core)) (wrap-file "resources") (wrap-file-info) (wrap-stacktrace))) (defn hello [] (str "Hello World!")) (defn start-server [] (run-jetty #'app {:port 8081 :join? false})) (defn -main [& args] (start-server)) 

注意从(defn app …)到(def app …)的更改

这对于使docker服务器正常工作至关重要

Compojure在内部使用环 (由同一作者), 环网服务器选项允许自动实时。 所以有两个select是:

 lein ring server lein ring server-headless lein ring server 4000 lein ring server-headless 4000 

注意 :

你需要在你的project.clj文件中有一行,如下所示:
:ring {:handler your.app/handler}