Skip to content

Commit 571f9bd

Browse files
committed
Add finding docs functionality from ClojureDocs
1 parent 93e90d5 commit 571f9bd

File tree

6 files changed

+187
-5
lines changed

6 files changed

+187
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* [#47](https://github.com/clojure-emacs/orchard/pull/46): [Java] Cache class-info for editable Java classes.
1111
* [#51](https://github.com/clojure-emacs/orchard/issues/51): Add basic xref functionality in `orchard.xref`.
1212
* [#64](https://github.com/clojure-emacs/orchard/issues/64): Port `cache-dir` from [soc/directories-jvm](https://github.com/soc/directories-jvm) to `orchard.directory`.
13+
* [#64](https://github.com/clojure-emacs/orchard/issues/64): Add finding docs functionality from ClojureDocs in `orchard.clojuredocs`.
1314

1415
### Changes
1516

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ TEST_SELECTOR := :java$(JAVA_VERSION)
1111

1212
TEST_PROFILES := +test
1313

14-
test:
14+
test-resources/clojuredocs/export.edn:
15+
curl -o $@ https://clojuredocs-edn.netlify.com/export.edn
16+
17+
test: test-resources/clojuredocs/export.edn
1518
lein with-profile +$(VERSION),$(TEST_PROFILES) test $(TEST_SELECTOR)
1619

17-
test-watch:
20+
test-watch: test-resources/clojuredocs/export.edn
1821
lein with-profile +$(VERSION),$(TEST_PROFILES) test-refresh $(TEST_SELECTOR)
1922

2023
# Eastwood can't handle orchard.java.legacy-parser at the moment, because

src/orchard/clojuredocs.clj

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
(ns orchard.clojuredocs
2+
"Find docs from ClojureDocs and retrieve the result as a map."
3+
{:author "Masashi Iizuka"
4+
:added "0.5.0"}
5+
(:require
6+
[clojure.edn :as edn]
7+
[clojure.java.io :as io]
8+
[clojure.string :as str]
9+
[orchard.directory :as dir])
10+
(:import
11+
(java.time Instant)))
12+
13+
(def cache (atom {}))
14+
(def default-edn-file-url
15+
"https://clojuredocs-edn.netlify.com/export.edn")
16+
(def cache-file-name
17+
(str/join dir/file-separator [(dir/cache-dir)
18+
"orchard"
19+
"clojuredocs"
20+
"export.edn"]))
21+
(def ^{:doc "One week. Unit is millisecond."}
22+
cache-updating-threshold 604800000)
23+
24+
(defn- write-cache-file! [url]
25+
(.. (io/file cache-file-name)
26+
getParentFile
27+
mkdirs)
28+
(->> url slurp (spit cache-file-name)))
29+
30+
(defn- map-from-key [f coll]
31+
(reduce #(assoc %1 (f %2) %2) {} coll))
32+
33+
(defn- load-cache-file! [cache-file]
34+
(let [docs (-> cache-file slurp edn/read-string)]
35+
(->> (:vars docs)
36+
(group-by #(:ns %))
37+
(reduce-kv #(assoc %1 %2 (map-from-key :name %3)) {})
38+
(reset! cache))
39+
true))
40+
41+
(defn load-cache!
42+
"Load exported documents file from ClojureDocs, and store it as a cache.
43+
A EDN format file is expected to the `export-edn-url` argument.
44+
45+
If `export-edn-url` is omitted, `default-edn-file-url` is used.
46+
47+
The loaded EDN file will be cached in `cache-file-name`.
48+
If the cached file is older than `cache-updating-threshold`,
49+
the cached file will be updated automatically."
50+
{:added "0.5.0"}
51+
([]
52+
(load-cache! default-edn-file-url))
53+
([export-edn-url]
54+
(let [cache-file (io/file cache-file-name)
55+
now-milli (-> (Instant/now) .getEpochSecond (* 1000))]
56+
(cond
57+
(or (not (.exists cache-file))
58+
(>= (- now-milli (.lastModified cache-file))
59+
cache-updating-threshold))
60+
(do (write-cache-file! export-edn-url)
61+
(load-cache-file! cache-file))
62+
63+
(empty? @cache)
64+
(load-cache-file! cache-file)))))
65+
66+
(defn clean-cache!
67+
"Clean a cached file and documents"
68+
{:added "0.5.0"}
69+
[]
70+
(.delete (io/file cache-file-name))
71+
(reset! cache {}))
72+
73+
(defn find-doc
74+
"Find a document matching to ns-name and var-name from cached documents.
75+
Cache will be updated when there are no cached documents or cached documents are old.
76+
77+
If `export-edn-url` is omitted, `default-edn-file-url` is used.
78+
79+
Return nil if there is no matching document."
80+
{:added "0.5.0"}
81+
([ns-name var-name]
82+
(find-doc ns-name var-name default-edn-file-url))
83+
([ns-name var-name export-edn-url]
84+
(load-cache! export-edn-url)
85+
(get-in @cache [ns-name var-name])))

test-resources/clojuredocs/export.edn

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

test/orchard/clojuredocs_test.clj

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
(ns orchard.clojuredocs-test
2+
(:require
3+
[clojure.java.io :as io]
4+
[clojure.test :as test :refer [deftest is testing use-fixtures]]
5+
[orchard.clojuredocs :as docs])
6+
(:import
7+
(java.time Instant)))
8+
9+
(def ^:private test-edn-file
10+
(io/resource "clojuredocs/export.edn"))
11+
12+
(defn- create-dummy-cache-file [& [timestamp]]
13+
(doto (io/file docs/cache-file-name)
14+
(spit (slurp test-edn-file))
15+
(cond-> timestamp (.setLastModified timestamp))))
16+
17+
(defn- clojuredocs-test-fixture [f]
18+
(with-redefs [docs/cache-file-name "target/clojuredocs/export.edn"]
19+
(docs/clean-cache!)
20+
(f)
21+
(docs/clean-cache!)))
22+
23+
(use-fixtures :each clojuredocs-test-fixture)
24+
25+
(deftest load-cache!-test
26+
(let [cache-file (io/file docs/cache-file-name)
27+
now (-> (Instant/now) .getEpochSecond (* 1000))
28+
new-timestamp (- now (/ docs/cache-updating-threshold 2))
29+
old-timestamp (- now docs/cache-updating-threshold)]
30+
(testing "No cache-file"
31+
(is (not (.exists cache-file)))
32+
(is (empty? @docs/cache))
33+
(docs/load-cache! test-edn-file)
34+
(is (.exists cache-file))
35+
(is (not (empty? @docs/cache))))
36+
37+
(testing "Old cache-file"
38+
(create-dummy-cache-file old-timestamp)
39+
(reset! docs/cache {:dummy "not-empty-dummy-data"})
40+
41+
(is (= old-timestamp (.lastModified cache-file)))
42+
(is (contains? @docs/cache :dummy))
43+
(docs/load-cache! test-edn-file)
44+
(is (< old-timestamp (.lastModified cache-file)))
45+
(is (not (contains? @docs/cache :dummy))))
46+
47+
(testing "Sufficiently new cache-file and no cached documents"
48+
(create-dummy-cache-file new-timestamp)
49+
(reset! docs/cache {})
50+
51+
(is (= new-timestamp (.lastModified cache-file)))
52+
(is (empty? @docs/cache))
53+
(docs/load-cache! test-edn-file)
54+
(is (= new-timestamp (.lastModified cache-file)))
55+
(is (not (empty? @docs/cache))))
56+
57+
(testing "Sufficiently new cache file and already cached documents"
58+
(create-dummy-cache-file new-timestamp)
59+
(reset! docs/cache {:dummy "not-empty-dummy-data"})
60+
61+
(is (= new-timestamp (.lastModified cache-file)))
62+
(is (contains? @docs/cache :dummy))
63+
(docs/load-cache! test-edn-file) ; Does nothing
64+
(is (= new-timestamp (.lastModified cache-file)))
65+
(is (contains? @docs/cache :dummy)))))
66+
67+
(deftest clean-cache!-test
68+
(create-dummy-cache-file)
69+
(reset! docs/cache {:dummy "not-empty-dummy-data"})
70+
(let [cache-file (io/file docs/cache-file-name)]
71+
(is (.exists cache-file))
72+
(is (not (empty? @docs/cache)))
73+
(docs/clean-cache!)
74+
(is (not (.exists cache-file)))
75+
(is (empty? @docs/cache))))
76+
77+
(deftest find-doc-test
78+
(testing "find existing document"
79+
(is (empty? @docs/cache))
80+
(is (not (.exists (io/file docs/cache-file-name))))
81+
(let [result (docs/find-doc "clojure.core" "first" test-edn-file)]
82+
(is (map? result))
83+
(is (every? #(contains? result %)
84+
[:arglists :doc :examples :name :notes :ns :see-alsos]))
85+
(is (.exists (io/file docs/cache-file-name)))
86+
(is (not (empty? @docs/cache)))))
87+
88+
(testing "find non-existing document"
89+
(is (nil? (docs/find-doc test-edn-file "non-existing-ns" "non-existing-var")))))

test/orchard/resource_test.clj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
(ns orchard.resource-test
22
(:require
3-
[clojure.test :refer [deftest testing is]]
3+
[clojure.string :as str]
4+
[clojure.test :refer [deftest is testing]]
45
[orchard.resource :as resource]))
56

67
(deftest resource-path-tuple-test
78
(is (nil? (resource/resource-path-tuple "jar:file:fake.jar!/fake/file.clj"))))
89

910
(deftest project-resources-test
1011
(testing "get the correct resources for the orchard project"
11-
(is (= "see-also.edn" (-> (resource/project-resources) first :relpath)))
12-
(is (= java.net.URL (-> (resource/project-resources) first :url class)))))
12+
(let [resources (->> (resource/project-resources)
13+
(remove #(str/ends-with? (.getAbsolutePath (:root %)) "test-resources")))]
14+
(is (= "see-also.edn" (-> resources first :relpath)))
15+
(is (= java.net.URL (-> resources first :url class))))))

0 commit comments

Comments
 (0)