clojure - 使用“wrap-reload”时如何避免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
参考给出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
然后将该路由器包装成一堆中间件,这些中间件将注入全局状态(如数据库字符串)以供处理程序使用。
那么如何添加wrap-reload
到此设置中呢?#'app
宏不适用于本地let
“变量”(或任何名称)。我需要将我的应用程序公开为全局变量吗?或者我可以在每个请求上重新生成整个闭包。
我的直觉告诉我要避免在模块主体中使用全局初始化代码并将所有代码保留在main
. 我应该只是将我的init-state
代码输入为全局变量并称之为一天,还是有更好的方法?
解决方案
我想出了一个我可以接受的解决方案。
(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 中工作正常。不需要全局状态:)
推荐阅读
- node.js - How can i simplify checking if a value exist in Json doc
- sql - 选择 sum sqlite 的最大值
- sql - 如何识别铸造列的错误?
- python-3.x - 使用 Python 根据列(4 列)的组合和金额(1 列)列的总和进行分组的最简单方法是什么?
- c++ - 在进入方法中推送新状态时状态机中的 unique_ptr 状态对象生命周期
- android - Firebase 使用 Firebase UI 身份验证登录错误
- php - PHP如何将整个数组添加到MongoDB文档字段中
- model-view-controller - 为 Windows 程序制作视图
- scala - Akka Stream 中的 Keep.both 示例
- matlab - 如何在 MATLAB 符号表达式中使用带索引的向量元素?