【问题标题】:Clojure: overriding one function in a libraryClojure:覆盖库中的一个函数
【发布时间】:2021-08-26 03:50:59
【问题描述】:

这个问题是几天前在previous question I asked 后面提出的。其中一个 cmets 是我应该放弃用于提取查询参数的 Ring 中间件并编写自己的。我认为我会使用的一种替代方法是利用现有的来获得我想要的东西,我一直在研究 Ring 源代码。它几乎完全符合我的要求。如果我写出我是如何理解它的工作原理的:

  1. 中间件具有调用参数请求的函数wrap-params
  2. params-requestparams 映射添加到 request 映射,调用 assoc-query-params
  3. assoc-query-params 最终在传入的查询字符串上调用 ring.util.codec/form-decode 将其转换为地图
  4. form-decode 使用 assoc-conj 通过 reduce 将值合并到现有映射中
  5. assoc-conj 的文档字符串说

将键与映射中的值相关联。如果密钥已经存在于 映射,值向量与键相关联。

最后一个函数是我上一个问题中存在问题的函数(TL;DR:我希望地图的值在字符串或向量类中保持一致)。戴上面向对象的帽子后,我可以通过子类化和覆盖我需要改变行为的方法来轻松解决这个问题。但是对于 Clojure,我看不到如何只替换一个函数而不必更改堆栈中的所有内容。这可能吗,是否容易,还是我应该以另一种方式这样做?如果涉及到它,我可以复制整个中间件库和编解码器,但对我来说似乎有点重量级。

【问题讨论】:

    标签: clojure ring


    【解决方案1】:

    虽然自定义中间件可能是解决此问题的最简单方法,但不要忘记您始终可以使用 with-redefs 覆盖 任何 函数。例如:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test))
    
    (dotest
      (with-redefs [clojure.core/range (constantly "Bogus!")]
        (is= "Bogus!" (range 9))))
    

    虽然这主要用于单元测试,但它是一个敞开的逃生舱,可用于覆盖任何功能。

    对于 Clojure,源代码中的 Var 与库中的 Var(甚至 clojure.core 本身,如示例所示)之间没有区别。

    【讨论】:

    • 正是我正在寻找的答案。非常感谢。
    【解决方案2】:

    我不同意不使用 Ring 的 param 中间件的建议。它为您提供有关传入参数的完美信息,因此如果您不喜欢 string-or-list 的默认行为,您可以随意更改参数。

    有很多方法可以做到这一点,但一种明显的方法是编写自己的中间件,并将其插入 Ring 的 param 中间件和您的处理程序之间。

    (defn wrap-take-last-param []
      (fn [handler]
        (fn [req]
          (handler 
            (update req :params 
                    (fn [params]
                      (into {} 
                         (for [[k v] params]
                           [k (if (string? v) v, (last v)]))))))))
    

    你可以写一些更花哨的东西,比如向函数添加一些参数,让你指定哪些参数只接收最后一个指定的参数,以及你希望始终作为列表接收的参数。在这种情况下,您可能不想将其包裹在整个处理程序中,而是分别包裹在每个路由周围以指定它们的预期参数。

    【讨论】:

    • 您的“更棒的东西”基本上是我在另一个问题中寻找的东西,我很惊讶我找不到其他人写的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 1970-01-01
    • 2014-09-08
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多