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

时间:2009-11-03 07:24:49

标签: clojure compojure

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

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

实现这一目标的最佳方法是什么?默认情况下,我的Compojure设置(使用带有Jetty的ant deps / ant的标准内容)似乎没有重新加载我做的任何更改。我将不得不重新启动run-server来查看更改。由于Java遗产和系统启动的方式等。这可能是完全正常的,当我从命令行启动系统时应该是这样。

但是,必须有一种方法可以在服务器运行时动态重新加载内容。我应该使用REPL的Compojure来实现我的目标吗?如果我应该,我怎么重新加载我的东西?

8 个答案:

答案 0 :(得分:38)

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

您需要两件主要的事情:

  1. 控件应返回REPL,以便您可以继续与服务器进行交互。这是通过添加{:join?启动Jetty服务器时的选项。
  2. 您希望在文件更改时自动获取某些命名空间中的更改。这可以通过Ring的“wrap-reload”中间件来完成。
  3. 玩具应用程序如下所示:

    (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函数使用检测列出的命名空间中的更改的函数来装饰您的应用程序路由。处理请求时,如果磁盘上的这些命名空间已更改,则会在进一步请求处理之前重新加载它们。 (我的“视图”命名空间是由Fleet动态创建的,因此每当它们发生变化时,它都会自动重新加载我的模板。)

    我添加了一些我发现始终有用的其他中间件。 wrap-file处理静态资产。 wrap-file-info设置这些静态资产的MIME类型。 wrap-stacktrace有助于调试。

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

答案 1 :(得分:22)

以下是我在Compojure Google小组中James Reeves得到的答案(答案在他的允许下):

您可以在使用时使用: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进行一些更改。在REPL类型:

user=> (use 'demo :reload) 
nil 

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

答案 2 :(得分:12)

我想补充一个答案,因为自最新答案以来我的情况有所改变,而且我花了一些时间自己寻找。

  1. 安装leiningen(只需按照其中的说明进行操作)

  2. 创建项目

    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 world文本,保存文件并在浏览器中重新加载页面。它应该反映新的文本。

答案 3 :(得分:5)

对Timothy与Jim Downing's setup的链接进行了跟进,我最近在enable automatic redeployment of compojure apps during development发现了对我发现的基线的重要补充。

答案 4 :(得分:4)

我有一个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")

每当我想更新服务器时,我都会从本地计算机创建一个ssh隧道到远程计算机。

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

答案 5 :(得分:3)

这是高度依赖于配置但对我有用,我认为你可以适应它:

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

  2. 启动clojure REPL

  3. 从compojure根目录

  4. 粘贴到hello.clj示例中
  5. 检查localhost:8080

  6. 重新定义欢迎 (defroutes greeter          (获取“/”                   (html [:h1“Goodbye World”]))))

  7. 检查localhost:8080

  8. 还有将REPL附加到现有进程的方法,或者您可以在服务器中嵌入套接字REPL,或者您甚至可以定义一个即时评估的POST调用,以允许您从浏览器重新定义函数本身!有很多方法可以解决这个问题。

答案 6 :(得分:1)

我想跟进mtnygard的回答并发布完整的project.clj文件和core.clj文件,它使得给定的功能正常工作。做了一些修改,它更加准系统

预设置命令

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

的变化

这对于让jetty服务器正常工作至关重要

答案 7 :(得分:0)

Compojure在内部使用ring(由同一作者),ring web server options允许自动重写。所以有两种选择:

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

请注意:

  

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

相关问题