【问题标题】:Creating Compojure routes from a list从列表中创建 Compojure 路由
【发布时间】:2011-08-14 07:16:45
【问题描述】:

我最近一直在玩 Compojure,并且我有一个小的基本 web 应用程序。对于我的 HTML 模板,我使用的是 Enlive,并且我有一个包含所有简单静态页面的名称空间。这些页面的 defroute 调用如下所示:

(defroutes public-routes
  (GET "/" []
    (info/index-template))
  (GET "/about" []
    (info/about-template))
  (GET "/contact" []
    (info/contact-template)))

我实际上还有更多,但这应该可以说明我在做什么。

现在,我想,这实际上只是我的一堆重复,所以我想我会尝试以下方法:

(defroutes info-routes
  (map #(GET (str "/" %) [] (ns-resolve 'webapp.pages.info
                                        (symbol (str % "-template"))))
       '("about" "contact")))

当然,这不起作用,因为映射返回的是惰性序列,而不是函数体 (?)。 有人知道我需要做什么才能让这个想法发挥作用吗?

或者我应该使用完全不同的方法来减少重复自己吗?

【问题讨论】:

    标签: clojure compojure


    【解决方案1】:

    defroutes is a macro 所以很遗憾你不能将它传递给像 map 这样的函数。您需要编写一个扩展为对 defroutes 的调用的宏。或者查看它扩展的函数并直接调用它们。

    在这样的 defroutes 调用中创建路由列表是行不通的

    (defroutes public-routes
      (make-list-of-routes)
    

    将扩展为路线列表:

    (defroutes public-routes
      ( (GET "/" [] (info/index-template)) 
        (GET "/about" [] (info/about-template))
        (GET "/contact" [] (info/contact-template))) )
    

    如果defroutes 是一个普通函数,你可以用apply 解决这个问题

    (apply defroutes (conj 'public-routes (make-list-of-routes)))
    

    因为defroutes 是一个宏,所以它在apply 可以运行之前就已经完全完成了,结果没有多大意义。你真的不能将宏组合成函数。宏 不是 clojure 中的一等公民(或我知道的任何 lisp) 当一些 Clojurians(通常不是我)说“宏是邪恶的”时,他们经常会想到这样的情况,当您尝试编写某个东西但不能编写它时,您会遇到这样一个事实。

    解决方法是不使用 defroutes 宏,直接调用 routes 函数。

    【讨论】:

    • 我不确定我是否误解了,但我没有将宏传递给映射,我试图将映射的结果传递给宏。据我了解,我的函数返回一个惰性函数序列,然后我希望 defroutes 处理这些函数。还是我错过了什么?
    • 按照 Arthur 的建议,我们可以发现 (defroutes r a b c)(def r (routes a b c)) 的快捷方式,其中 routes 是(幸运的是!)一个普通函数。您的示例以 (routes [a b c]) 结尾(使用 seq 而不是向量,但您明白了)而不是 (routes a b c)。这可以通过 apply:(def r (apply routes [a b c])) 来解决。如果routes 是一个宏,这当然是行不通的。
    【解决方案2】:

    您始终可以使用 defroutes 使用的routes 函数:

    (defroutes info-routes
      (apply routes
        (map #(GET (str "/" %) [] 
                   (ns-resolve 'webapp.pages.info
                               (symbol (str % "-template"))))
             '("about" "contact"))))
    

    但这仍然很无聊,让我们为它增添趣味! ;-)

    (defn templates-for [& nss]
      (->> nss
           (map ns-publics)
           (apply concat)
           (filter #(->> % first str
                         (re-seq #"-template$")))
           (map second)))
    
    (defn template-uri [template]
      (->> template meta :name name
           (re-seq  #"(.*)-template$")
           first second (str "/")))
    
    (defn template->route [template]
      (GET (template-uri template) [] template))
    
    (defroutes public-routes
      (GET "/" [] "foo")
      (apply routes (map template->route
                         (templates-for 'webapp.pages.info))))
    

    使用此代码,templates-for 函数将在给定的命名空间中查找以“-template”结尾的任何函数,并使用它们编写适当的路由。看看我没有使用任何宏,而是使用大量组合。

    【讨论】:

    • 我确实遇到了一些问题,因为现在传入了一个参数,而我之前没有传递一个参数,但我只是将我的 defsn-p 参数列表修复为 [_] 现在它可以完美运行.谢谢!
    猜你喜欢
    • 2011-04-23
    • 2013-04-20
    • 2014-03-25
    • 2021-07-19
    • 2012-10-11
    • 1970-01-01
    • 2013-12-21
    • 2012-05-31
    • 1970-01-01
    相关资源
    最近更新 更多