2013-03-24 38 views
5

http://mindbat.com/2013/03/clojurewest-2013-day-one-notes/有一张纸条,上面写着:如何在应用程序的Compojure摆脱全球数据

  • 在顶层def'ing裁判和原子基本上是通过全球的单身可变状态,请避免
  • 推荐使用构造函数来您想使用的状态变量,然后传递状态一起到各功能

我觉得这是很好的建议,但我不完全知道如何在Ring/Compojure应用程序中执行此操作。任何人都可以给出一个具体的例子,说明这将如何工

我特别感兴趣的是如何结合defroutes,initapp这样一起去掉那个范围内的全局变量。

回答

2

有你需要的全局状态,因此你无法避免这一点很多情况下,你可以做什么管理得当,我想这就是2点讲:

不是一个好办法:

(ns data) 
(def users (atom [])) 

(ns pages) 
(defn home [] 
    (do-something data/@users) 

(defn save [] 
    (let [u @users] 
     (swap! data/users ....) 

好方法:

(ns data) 
(def- users (atom [])) 
(defn get-users [] @users) 
(defn update-user [user] (swap! @users ...)) 

(ns pages) 
; use the functions exposed by data ns to interact with data rather then poking the atom directly. 

基本上所有的访问任何类型的国家应与其他被抽象掉部分应用程序。你所有的业务逻辑函数都应该将状态作为参数并返回新的状态,而不是直接选择状态并更新它。

+0

y!这是一个好方法。 – hsestupin 2013-03-25 05:55:46

+0

@hsestupin:这是一个问题或肯定:) – Ankur 2013-03-25 06:06:29

+0

肯定c。我的意思是,当国家不能通过直接引用而改变时,我完全同意这种做法。 – hsestupin 2013-03-25 08:52:40

5

我从斯图尔特的谈话了解什么是这样的:

(ns state.core) 

(defn create-user-module [] (atom [])) 

(defn add-user [module user] 
    (swap! module conj user)) 

(defn get-users [module] 
    @module) 

现在有你的“芯”作为操作状态的功能没有全局状态指望得到它作为一个参数。这些可以轻松进行测试,因为您可以为每个测试创建一个“用户模块”的新实例。另外,这个模块的客户端不应该关心他们在create-user-module函数中获得什么,他们应该只是传递它而不检查它,这样你可以随时更改用户模块的实现。如果您要有多个实现,Stuart还会谈谈如何为这些模块创建协议。

试图回答你的问题,一个环适配器仅1个PARAM的功能,并且的Compojure只是一个路由库,因此你可以使用闭包,如创建一个web应用程序:

(ns state.web 
    (:use compojure.core) 
    (:require [state.core :as core])) 

(defn web-module [user-module] 
    (routes 
    (GET "/all" [] (core/get-users user-module)))) 

现在你可以调用Web模块来创建Web应用程序,并将其作为参数传递给需要的依赖项。当然,你仍然需要有人来创建正确的用户模块的Web应用程序,所以你只需要一个电线都在一起了“主要”功能:

(ns state.main 
    (:require state.core 
      state.web) 
    (:use ring.adapter.jetty)) 

(defn start [] 
    (let [user-module (state.core/create-user-module) 
     web-module (state.web/web-module user-module)] 
    (run-jetty web-module {:port 3000 :join? false}))) 

(defn stop [app] 
    (.stop app)) 

start将您的应用程序main方法被调用。这只是意味着你需要切换到lein-run插件。

现在,鉴于您询问的是init(我假设来自lein ring插件),我想您打算将webapp部署到容器中。由于雷音环插件拥有了Java Servlet FW限制范围内,并且该处理结束编译为Java Servlet的工作,或许您可以做的最好的是一样的东西:

(ns state.web 
    (:use compojure.core) 
    (:require [state.core :as core])) 

(def module-deps (atom {}) 

(defn init-app [] (swap! module-deps conj [:user-module (core/create-user-module)])) 

(defroutes web-module [] 
    (GET "/all" [] (core/get-users (:user-module @module-deps)))) 

这仍然意味着你的核心命名空间很容易测试,但是您仍然在Web命名空间中拥有全局状态,但是我认为这是“正确”封装的,如果您必须使用java容器,则可能已经足够好了。

而这只是为什么图书馆比框架“更好”的另一个理由:)