【问题标题】:How to avoid global state in clojure when using "wrap-reload"?使用“wrap-reload”时如何避免clojure中的全局状态?
【发布时间】:2019-06-01 01:36:01
【问题描述】:

我正在学习 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 引用的形式给出wrap-reload。在每个请求上,wrap-reload 将重新加载整个命名空间,然后重新解析 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-routes 基于compojure 库生成路由器,make-app-stack 然后将该路由器包装成一堆中间件,这些中间件将注入全局状态(如 DB 字符串)以供处理程序使用。

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

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

【问题讨论】:

    标签: clojure compojure ring


    【解决方案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 中工作正常。不需要全局状态:)

    【讨论】:

      猜你喜欢
      • 2010-09-07
      • 1970-01-01
      • 2013-06-05
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      • 2018-09-17
      相关资源
      最近更新 更多