提问者:小点点

使用“重新加载”时,如何避免Clojure中的全局状态?


我正在学习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引用的形式给出重新加载。在每个请求中,重新加载将重新加载整个命名空间,然后将handler引用重新解析为一个潜在的不同函数。或者至少这是我的理解。

在这个简单的例子中,这一切都很好。当我用我的实际处理程序替换这个hello world处理程序时,问题就开始了,它绑定了各种状态(例如数据库连接)。这个状态在-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-路由基于compojure库生成路由器,然后make-app-ack将这个路由器包装成一堆中间件,中间件将注入全局状态(如DB字符串)供处理程序使用。

那么如何将重新加载添加到此设置中呢?#'app宏在本地let“变量”(或任何它的调用)上不起作用。我需要将我的应用公开为全局变量吗?或者我可以在每个请求上重新生成整个闭包。

我的直觉告诉我要避免在模块主体中包含全局初始化代码,并将所有代码保留在main中,但也许我想多了。我是否应该将我的init-state代码作为全局变量键入并收工,或者有更好的方法吗?


共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

我没有直接使用重新加载,而是使用底层的私有重新加载函数。我必须在每个请求中重新创建路由堆栈,但它似乎在dev中工作得很好。不需要全局状态:)