簡體   English   中英

沒有重新啟動Web服務器的Compojure開發

[英]Compojure development without web server restarts

我之前在Clojure中編寫了一個小型Swing應用程序,現在我想創建一個Ajax風格的Web應用程序。 Compojure現在看起來是最好的選擇,所以這就是我要嘗試的。

我想要一個真正的小編輯/嘗試反饋循環,所以我不希望在每次小改變后重啟web服務器。

實現這一目標的最佳方法是什么? 默認情況下,我的Compojure設置(使用帶有Jetty的ant deps / ant的標准內容)似乎沒有重新加載我做的任何更改。 我將不得不重新啟動run-server來查看更改。 由於Java遺產和系統啟動的方式等。這可能是完全正常的,當我從命令行啟動系統時應該是這樣。

但是,必須有一種方法可以在服務器運行時動態地重新加載內容。 我應該使用REPL的Compojure來實現我的目標嗎? 如果我應該,我怎么重新加載我的東西?

這是一個相當古老的問題,最近有一些變化使這更容易。

你想要兩個主要的東西:

  1. 控件應返回到REPL,以便您可以繼續與服務器進行交互。 這是通過添加{:join? 啟動Jetty服務器時的選項。
  2. 您希望在文件更改時自動獲取某些命名空間中的更改。 這可以通過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函數使用檢測列出的命名空間中的更改的函數來裝飾您的應用程序路由。 處理請求時,如果磁盤上的這些命名空間已更改,則會在進一步請求處理之前重新加載它們。 (我的“視圖”命名空間是由Fleet動態創建的,因此每當它們發生更改時,它都會自動重新加載我的模板。)

我添加了一些我發現始終有用的其他中間件。 wrap-file處理靜態資產。 wrap-file-info設置這些靜態資產的MIME類型。 wrap-stacktrace有助於調試。

從REPL,您可以通過使用命名空間並直接調用start-server來啟動此應用程序。 :gen-class關鍵字和-main函數意味着app也可以從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進行一些更改。 在REPL類型:

user=> (use 'demo :reload) 
nil 

您的更改現在應顯示在http:// localhost:8080上

我想添加一個答案,因為自最新的答案以來我已經有了一些改變,我花了一些時間自己尋找。

  1. 安裝leiningen (只需按照那里的說明)

  2. 創建項目

     lein new compojure compojure-test 
  3. 編輯project.clj的ring部分

     :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文本,保存文件並在瀏覽器中重新加載頁面。 它應該反映新的文本。

繼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")

每當我想更新服務器時,我都會從本地機器到遠程機器創建一個ssh隧道。

Enclojure和Emacs(運行SLIME + swank-clojure)可以連接到遠程REPL。

這是高度配置依賴,但對我有用,我認為你可以適應它:

  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根目錄粘貼hello.clj示例

  4. 檢查localhost:8080

  5. 重新定義歡迎(defroutes greeter(GET“/”(html [:h1“Goodbye World”]))))

  6. 檢查localhost:8080

還有將REPL附加到現有進程的方法,或者您可以在服務器中嵌入套接字REPL,或者您甚至可以定義一個即時評估的POST調用,以允許您從瀏覽器本身重新定義函數! 有很多方法可以解決這個問題。

我想跟進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服務器正常工作至關重要

Compojure在內部使用ring (由同一作者), 環網服務器選項允許自動realoading。 所以有兩種選擇:

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

注意 :

你需要在project.clj文件中有一行看起來像:
:ring {:handler your.app/handler}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM