ClojureScript 中的 eval-str 以 public 结构作为参数

eval-str in ClojureScript with public structure as a parameter

我的问题需要将自定义逻辑函数应用于结构。这些函数作为字符串存储在数据库中。我有这样的数据:

(def fruits {:apple {:color "red" :ripe? true}
             :strawberry {:color "red" :ripe? false}})

我有这个条件检查:

"(some (fn [fruit] (-> fruit val :ripe? false?)) fruits)"

不幸的是,即使我尝试了各种方法,我也无法做到这一点:

1)

(cljs/eval-str (cljs/empty-state)
           "(some (fn [fruit] (-> fruit val :ripe? false?)) my.main/fruits)"
           ""
           {:eval cljs/js-eval}
           identity)

这有效但会产生错误:

WARNING: No such namespace: my.main, could not locate my/main.cljs, my/main.cljc, or Closure namespace "" at line 1
WARNING: Use of undeclared Var my.main/fruits at line 1

而且这种方法在高级编译中显然行不通。

2) 我尝试利用在 Clojure 中有效的方法:

((eval
   (read-string
     "(fn [fruits]
       (some (fn [fruit] (-> fruit val :ripe? false?)) fruits))"))
  fruits)

我不明白为什么这在高级编译中不起作用。不幸的是,它每次都只是 returns nil

是只有我没有想出解决方案还是 CLJS 还没有能力做到这一点?

我怀疑您将很难通过以下方式实现您的要求 这种方法。大问题可能是由于方式 clojurescirpt 需要编译成 javascript(使用 Google 闭包)。你 可能可以让它工作 用外部设备做一些聪明的事情并使用低 级别 javascript 互操作和闭包库,但我怀疑它会很难 工作。

可能值得考虑的几种替代方法

  • 将数据以edn格式存储在数据库中。使用 edn,您可以 把它读入一个var然后执行它
  • 改变方向 - 你真的需要存储完整的功能吗? 而是定义一种 DSL 类型,它从数据库中获取参数 将提供必要的动态执行级别。
  • 你能有某种预处理解决方案吗,即将函数写在 clojurescript,但使用闭包功能将其编译为 javascript 和 将其插入数据库而不是原始 clojurescript。这个会 使数据的初始存储更加复杂,但可以简化调用 运行时的动态函数。您甚至可以包括一些代码检查或 验证以减少从数据库中获取代码的可能性 错了。

使用完全动态的代码有很多风险,几乎 从来没有一个好的解决方案。除了您遇到的众多安全问题 这种做法,你还得优雅地处理出现的处理问题 来自被插入数据库的错误定义(即错误函数 使您的应用程序崩溃或损坏数据的定义。如果你只需要拥有 动态执行未知代码的能力,那么至少 edn 提供了一些 使用 eval-str 无法获得额外的保护 - 但实际上,不要这样做 它。

经过数小时的实验并努力从字符串中评估函数,我决定编写 DSL。

在数据库中,我使用包含这些参数的映射存储字符串:

  • :在哪里? - 包含所需答案路径的矢量。
  • :什么? - 我正在寻找的答案。
  • :严格? (可选)- 一个布尔值。如果为真,则答案需要与 :what? 规则完全相同(顺序无关紧要)。

然后我只评估那个简单的 cljs 文件。它适用于 advancednone 优化模式。

(defn standard-cond-met? [{:keys [what? where? strict?]
                           :or {strict? false}}]
  (let [answer (get-in answers (conj where? :values))]
    (if strict?
      (= (sort what?) (sort answer))
      (clojure.set/subset?
        (set what?)
        (set answer)))))