Передача состояния в качестве параметра в обработчик кольца?
Как удобно вводить состояние в обработчики меток (без использования глобальных варов)?
Вот пример:
(defroutes main-routes
(GET "/api/fu" [] (rest-of-the-app the-state)))
(def app
(-> (handler/api main-routes)))
Я хотел бы получить the-state
в обработчик compojure для main-routes
. Состояние может быть чем-то вроде карты, созданной с помощью:
(defn create-app-state []
{:db (connect-to-db)
:log (create-log)})
В нелокальном приложении я создам состояние в основной функции и начну вводить его или его части в качестве функциональных параметров для разных компонентов приложения.
Можно ли что-то подобное сделать с помощью функции ring :init
без использования глобального var?
Ответы
Ответ 1
Я видел, как это делалось несколькими способами. Первый использует промежуточное программное обеспечение, которое вводит состояние в качестве нового ключа в карте запроса. Например:
(defroutes main-routes
(GET "/api/fu" [:as request]
(rest-of-the-app (:app-state request))))
(defn app-middleware [f state]
(fn [request]
(f (assoc request :app-state state))))
(def app
(-> main-routes
(app-middleware (create-app-state))
handler/api))
Другой подход заключается в замене вызова на defroutes
, который за кулисами создаст обработчик и назначит его var, с функцией, которая примет какое-то состояние, а затем создаст маршруты, введя состояние в качестве параметров для вызова функций в определениях маршрутов:
(defn app-routes [the-state]
(compojure.core/routes
(GET "/api/fu" [] (rest-of-the-app the-state))))
(def app
(-> (create-app-state)
app-routes
api/handler))
Учитывая выбор, я бы, вероятно, пошел со вторым подходом.
Ответ 2
"Правильный" способ сделать это - использовать динамически связанный var. Вы определяете var с помощью:
(def ^:dynamic some-state nil)
И затем вы создаете промежуточное промежуточное звено, которое связывает var для каждого вызова обработчика:
(defn wrap-some-state-middleware [handler some-state-value]
(fn [request]
(bind [some-state some-state-value]
(handler request))))
Вы использовали бы это для установки зависимостей, используя это в своей "основной" функции, где вы запускаете сервер:
(def app (-> handler
(wrap-some-state-middleware {:db ... :log ...})))