使用“自动换行”时如何避免Clojure中的全局状态?

时间:2019-01-05 21:47:58

标签: clojure compojure ring

我正在学习Clojure,在尝试重构我的Web应用程序以使其更具功能性并减少对全局状态的依赖时遇到了麻烦。

使用ring框架制作简单的自动重新加载服务器的方式如下:

(defn handler [req]
  {:status 200
   :body "<h1>Hello World</h1>"
   :headers {}})

(defn -main []
  (jetty/run-jetty (wrap-reload #'handler)
     {:port 12345}))

(来源:https://practicalli.github.io/clojure-webapps/middleware-in-ring/wrap-reload.html

所以handler是全局函数。它作为对var的{​​{1}}引用给出。在每个请求上,wrap-reload将重新加载整个名称空间,然后重新解析对wrap-reload引用的潜在功能。或者至少这是我的理解。

在这个简单的示例中,这一切都很好。当我用实际的处理程序替换此hello world处理程序时,麻烦就开始了,该处理程序绑定了各种状态(例如数据库连接)。此状态在handler内部初始化一次,然后注入到路由栈中。像这样:

-main

(defn init-state [args dev] (let [db (nth args 1 "jdbc:postgresql://localhost/mydb") routes (make-routes dev) app (make-app-stack routes db) {:port (Integer. (nth args 0 3000)) :route routes :dev dev :db db :app app}) (defn start [state] (println "Running in " (if (:dev state) "DEVELOPMENT" "PRODUCTION") " mode") (model/create-tables (:db state)) (jetty/run-jetty (:app state) {:port (:port state)})) (defn -main [& args] (start (init-state args false))) 根据make-routes库生成路由器,然后compojure将此路由器包装为一堆中间件,这些中间件将注入全局状态(如DB字符串)以供处理程序。

那么如何将make-app-stack添加到此设置? wrap-reload宏不适用于本地#'app“变量”(或其任何调用)。我需要将我的应用公开为全局变量吗?或者我可以根据每个请求重新生成整个闭包。

我的直觉告诉我,避免在模块主体中使用全局初始化代码,而将所有代码都保留在let中,但是也许我想得太过分了。我应该只将main代码输入为globals并命名为一天,还是有更好的方法?

1 个答案:

答案 0 :(得分:1)

我想出了一个可以接受的解决方案。

(ns webdev.core
  (:require [ring.middleware.reload :as ring-reload]))

; Defeat private defn
(def reloader #'ring-reload/reloader)

; Other stuff...

(defn load-settings [args dev]
  {:port (Integer. (nth args 0 3000))
   :db (nth args 1 "jdbc:postgresql://localhost/webdev")
   :dev dev})

(defn make-reloading-app [settings]
  (let [reload! (reloader ["src"] true)]
    (fn [request]
      (reload!)
      ((make-app settings) request))))

(defn start [settings]
  (let
    [app
      (if (:dev settings)
        (make-reloading-app settings)
        (make-app settings))]
    (println "Running in " (if (:dev settings) "DEVELOPMENT" "PRODUCTION") " mode")
    (model/create-tables (:db settings))
    (jetty/run-jetty app {:port (:port settings)})))

(defn -main [& args]
  (start (load-settings args false)))

此处提供完整代码:https://github.com/panta82/clojure-webdev/blob/master/src/webdev/core.clj

我不是直接使用wrap-reload,而是使用底层的私有reload函数。我必须在每个请求中都重新创建路由堆栈,但在dev中似乎工作正常。不需要全局状态:)