如何在 clojure 网络应用程序中有条件地加载功能

How can I conditionally load functionality in a clojure web-app

我有一个 clojure 网络应用程序(码头服务器上的标准环处理程序和 compojure 路由),我为此启用了实时资产重新编译作为中间件,这在开发中非常方便。当我们接近生产时,我想找到一种方法,不在生产中加载该代码,而是读取预编译的资产(我可以将其作为 lein 任务生成)。

目前资产编译机制存在于项目代码中——它可以使用 eval-in-project 从 lein 任务加载,所以我可以在两个地方重用相同的代码。然而,这意味着不需要的文件被编译并包含在生产应用程序中。

另一个问题是我正在使用的一种资产编译工具会导致应用程序无法在初始化时加载,如果 uberjar'ed 因为它使用了 v8 的本机绑定,这是不可用的 (并且不需要)当预编译的资产可用时。

我怎样才能避免在生产 uberjar 中加载此代码,但在开发和测试期间 运行 时仍然受益于动态重新编译?

您在 Leiningen 中的 :source-paths 密钥决定了在哪些目录中检查 Clojure 源代码。使用 :source-paths 的每个环境设置,您可以防止不需要的命名空间包含在部署的 uberjar 中。

下一个难题是确保您的代码不依赖于生产实例上的开发代码。这可以在 environ 库的帮助下完成。

; excerpt of project.clj
(defproject your-org/your-project "version"
   :source-paths ["src"] ; the main source location
   :profiles {:dev {:source-paths ["dev-src"] ; added directory
                    :env {:dev "true"}}}
 ...)

; excerpt of project code for src/your_org/your_project.clj
(ns your-org.your-project
  (:require environ.core :refer [env]))

(def maybe-launch-optional-thing
  (if (= (env :dev) "true") ; checking a profile specific value
   (do (require 'dev-only-dep.core)
       (resolve 'dev-only-dep/launch))
   (constantly nil))

...

(defn -main
  [& args]  
  (maybe-launch-optional-thing)
  ...)

if包裹了requireresolve的用法,无论dev-only-dep.core可用与否,确保此代码有效。 maybe-launch-optional-thing 绑定到 :dev 配置文件下可选命名空间中的适当函数,否则为空操作。