【问题标题】:In Clojure, is there an idiomatic way of destructuring a map in a macro definition?在 Clojure 中,是否有一种惯用的方式在宏定义中解构地图?
【发布时间】:2013-03-16 08:07:52
【问题描述】:

我一直在 Web 项目中使用 noir,我想根据用户对 defpage 宏定义的所有可能路由的访问级别(和子级别)来限制对用户的访问。所以最初我有

(defpage [:post "/mysite"] {:as input}
  (if-not (has-reqd-user-level? :levelX :sublevelY "/grantedRoute")
    (noir.response/redirect "/insufficientRights")
    ...))

然后我认为这会摆脱样板代码:

(defmacro defpage-with-user-level [level sublevel granted-route route data expr]
  `(defpage ~route ~data
     (if-not (has-reqd-user-level? ~level ~sublevel ~granted-route)
       (noir.response/redirect "/insufficientRights")
       ~expr)))

最后,我们使用如下:

(defpage-with-user-level :levelX :sublevelY "/grantedRoute"
  [:post "/mysite"] {:as input}
  (html
    [:body [:h1 (str "Hello " (:name input) "!")]]))

但正如 Rich Hickey https://groups.google.com/forum/#!msg/clojure/4II-HKr_Pu0/2IcKit99cagJ 的这篇文章中提到的,由于位置绑定,这感觉有点尴尬,当已经存在地图时,这不是惯用的。

但是,我一直在寻找一些关于在宏中使用解构绑定的示例或讨论,遗憾的是,我没有发现它们的任何明确用途,因为它一直在传递未计算的表达式。

于是,我想到了以下解决方案:

(defmacro defpage-with-user-level [dts expr]
  `(defpage (:route ~dts) (:data ~dts)
     (if-not (has-reqd-user-level? (:level ~dts) (:sublevel ~dts) (:granted-route ~dts))
       (noir.response/redirect "/insufficientRights")
       ~expr)))

但是现在,还不清楚如何将映射本地的数据映射从 :get 和 :post 传递到上面的示例中的本地。

我的第一次尝试没有被篡改,还是我真的需要使用第二种方法?我希望不是。还有其他选择吗?请告诉我。

【问题讨论】:

    标签: map macros clojure noir destructuring


    【解决方案1】:

    您的第一个解决方案很好。 Rich 所说的是使用普通的旧地图来传递数据,而不是为每种类型的数据创建新的类型/类。例如:您可以使用简单的地图来表示用户信息,而不是创建一个类来表示用户数据。

    就您的第二次尝试而言,您可以在宏中使用地图解构:

    (defmacro defpage-with-user-level [{:keys [route data level sublevel granted-route]} expr]
      `(defpage ~route ~data
         (if-not (has-reqd-user-level? ~level ~sublevel ~granted-route)
           (noir.response/redirect "/insufficientRights")
           ~expr)))
    
    (defpage-with-user-level {:level :levelX 
                              :sublevel :sublevelY 
                              :granted-route "/grantedRoute"
                              :route [:post "/mysite"] 
                              :data {:as input}}
      (html
        [:body [:h1 (str "Hello " (:name input) "!")]]))
    

    【讨论】:

    • 非常感谢!这正是我想知道的。那么,我应该坚持您提出的解决方案还是应该使用我的第一次尝试?基本上,正如您在 Rick 的帖子中提到的那样,我们将页面数据作为地图封装,所以我想您的更正确。
    • 顺便说一句,这家伙是 Rich 而不是 Rick ;)
    • 太棒了!对不起...谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    • 2019-01-25
    相关资源
    最近更新 更多