如何查找包含特定元数据的所有变量

时间:2013-11-06 11:31:37

标签: clojure metadata

假设我已将特定元数据添加到我的变种中:

(defn ^:run-at-startup init []
  (prn "Initializing...")
  :done)

(meta (var init))
; {:arglists ([]), :ns #<Namespace user>, :name init, :end-column 34, 
; :run-at-startup true, :column 1, :line 5, :file "NO_SOURCE_FILE", :end-line 5}

然后我想查找包含它的所有变量(跨越不同的名称空间)。有可能吗?

这就是原因。我的应用程序包含几个必须在启动时初始化的模块。可以添加新模块并删除现有模块(当然不是在运行时),并且必须在不知道模块的任何细节的情况下调用它的初始化程序。我想把元数据添加到初始化器中,然后查看它并调用它。

我想知道是否有更好的方法。

1 个答案:

答案 0 :(得分:3)

因此,如果require包含非私有初始值设定项的所有命名空间,all-ns都能够检索这些命名空间的列表。如果您不知道存在哪些命名空间,则可以使用例如tools.namespace找出来。

以下函数查找包含某个元数据键设置为true的所有变量,并返回变量值的seq。

(defn find-by-var-meta
  [metadata-flag]
  (->> (all-ns)
       (mapcat ns-publics)
       (keep
         (fn [[_ v]]
           (when (-> v meta metadata-flag)
             (var-get v))))))

然后可以遍历生成的seq,并且可以调用函数的所有内容。所以,在你的情况下,这应该是这样的:

(require '[my.namespace.initializers a b c])
(find-by-var-meta :run-at-startup) ;; => seq of initializers from the above ns.

快速查看REPL:

(defn ^:run-at-startup add-one [x] (inc x))    ;; => #'user/add-one
((first (find-by-var-meta :run-at-startup)) 5) ;; => 6

(如此处所示,如果您只想将一个或多个密钥设置为true,则也无需为元数据指定完整的地图。)