Clojure単独ネタ。Namespaceに定義されたvarをスキャンして、そのaddedメタデータからClojureに追加されたバージョンとvarの一覧みたいなものが作れないかなぁということを、ちょっと試してみたくなりまして。
結論からいくと、こんな感じのソースでできました。
find_added_sources.clj
(import '(clojure.lang Var)) (defn filter-vars [target-namespaces pred] (filter pred (for [n target-namespaces mv (vals (. n getMappings)) :when (instance? Var mv)] mv))) ;; 全Namespaceから検索 (def target-namespaces (all-ns)) ;; 指定のNamespaceのシーケンスから検索 ;;(def target-namespaces [(find-ns 'clojure.core) ;; (find-ns 'clojure.string)]) (doseq [n (filter-vars target-namespaces #(and (not (nil? ((meta %) :added))) (>= (compare ((meta %) :added) "1.4") 0)))] (println (str n " => added[" ((meta n) :added) "]")))
これを実行すると、こんな結果が得られます。
$ clj find_added_sources.clj #'clojure.core/cond->> => added[1.5] #'clojure.core/send-via => added[1.5] #'clojure.core/set-agent-send-executor! => added[1.5] #'clojure.core/mapv => added[1.4] #'clojure.core/*data-readers* => added[1.4] #'clojure.core/filterv => added[1.4] #'clojure.core/some-> => added[1.5] #'clojure.core/reduced? => added[1.5] #'clojure.core/default-data-readers => added[1.4] #'clojure.core/as-> => added[1.5] #'clojure.core/ex-info => added[1.4] #'clojure.core/reduced => added[1.5] #'clojure.core/*default-data-reader-fn* => added[1.5] #'clojure.core/reduce-kv => added[1.4] #'clojure.core/*compiler-options* => added[1.4] #'clojure.core/ex-data => added[1.4] #'clojure.core/set-agent-send-off-executor! => added[1.5] #'clojure.core/some->> => added[1.5] #'clojure.core/cond-> => added[1.5] #'clojure.core/cond->> => added[1.5] 〜省略〜
ここでは、addedメタデータに「1.4」以上の値が書かれているものを対象にしています。
説明ですが、ClojureのNamespaceの実体はclojure.lang.Namespaceというクラスに定義されていて、Namespace内にあるものはmappingsというフィールド(Mapです)に格納されています。
そして、varはclojure.lang.Varというクラスとなっています。
ですので、Namespaceのmappings内からVarのみ抽出しました。
(for [n target-namespaces mv (vals (. n getMappings)) :when (instance? Var mv)] mv)))
あとはそれを、任意の述語でフィルタリングしています。
今回のお題から、以下のような述語としました。
#(and (not (nil? ((meta %) :added))) (>= (compare ((meta %) :added) "1.4") 0)))]
meta関数を使い、varからメタデータを抽出し、addedがあり、かつ指定の文字列より大きい値であればtrueとなるようにしています。
最後に、それを表示しているだけです。
(doseq [n (filter-vars target-namespaces #(and (not (nil? ((meta %) :added))) (>= (compare ((meta %) :added) "1.4") 0)))] (println (str n " => added[" ((meta n) :added) "]")))
なお、Namespaceは今回全てということでall-ns関数を使いました。all-ns関数を使用すると、全Namespaceがシーケンスとして返却されます。
;; 全Namespaceから検索 (def target-namespaces (all-ns))
任意のNamespaceに対して行う場合は、find-ns関数を使うとよいでしょう。
;; 指定のNamespaceのシーケンスから検索 (def target-namespaces [(find-ns 'clojure.core) (find-ns 'clojure.string)])
ただ、all-ns関数を使った場合でもReducersみたいなrequireが必要なものについては、明示的に先にrequireしておかないと対象に含まれないので、その点はご注意を。
また、そもそもaddedメタデータが付与されていなければ、単に無視されるだけになります。