Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link posts #90

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,26 @@ Resources for understanding and testing social sharing:
- [LinkedIn Post Inspector](https://www.linkedin.com/post-inspector/)
- [Twitter Card Validator](https://cards-dev.twitter.com/validator)

### Post links

quickblog adds `prev` and `next` metadata to each post (where `prev` is the
previous post and `next` is the next post in date order, oldest to newest). You
can make use of these by adding something similar to this to your `post.html`
template:

``` html
{% if any prev next %}
<div class="post-prev-next">
{% if prev %}
<div>⏪ <a href="{{prev.file|replace:.md:.html}}">{{prev.title}}</a></div>
{% endif %}
{% if next %}
<div><a href="{{next.file|replace:.md:.html}}">{{next.title}}</a> ⏩</div>
{% endif %}
</div>
{% endif %}
```

## Templates

quickblog uses the following templates in site generation:
Expand Down
4 changes: 3 additions & 1 deletion src/quickblog/api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,9 @@
:as opts}
(-> opts
(assoc :watch (format "<script type=\"text/javascript\" src=\"%s\"></script>"
lib/live-reload-script))
lib/live-reload-script)
;; Include preview posts in post linking (prev and next)
:include-preview-posts? true)
apply-default-opts
render)]
(reset! posts-cache (:posts opts))
Expand Down
58 changes: 49 additions & 9 deletions src/quickblog/internal.clj
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,45 @@
(println error)
true))

(defn- delink-prev
"Make sure we don't have a chain of previous posts"
[post]
(update post :prev dissoc :prev))

(defn link-posts-reduce-f [{:keys [posts prev] :as acc} post]
(let [cur (when prev
(-> prev (assoc :next post) delink-prev))
post' (if prev
(assoc post :prev prev)
post)
posts' (if cur
(conj posts cur)
posts)]
{:posts posts', :prev post'}))

(defn link-posts
"For a map of files to posts, sorts them adds to each post a :prev key pointing
to the previous post and a :next key pointing to the next post, where previous
and next are defined by the order imposed by `sort-posts`. Preview posts are
skipped unless `:include-preview-posts?` is set true in `opts`."
[{:keys [include-preview-posts?]} posts]
(->> (let [maybe-remove-previews (if include-preview-posts? identity remove-previews)
ps (->> posts
vals
maybe-remove-previews
sort-posts
reverse
(reduce link-posts-reduce-f {:posts [], :prev nil}))]
(-> ps :posts (conj (-> ps :prev delink-prev))))
(map (fn [{:keys [file] :as post}]
[file post]))
(into {})
;; We need to merge this with the original posts map because that
;; map might contain preview posts, whereas the linking process
;; intentionally skips preview posts unless the `include-preview-posts?`
;; param is true
(merge posts)))

(defn load-posts [{:keys [cache-dir cached-posts posts-dir] :as opts}]
(if (fs/exists? posts-dir)
(let [cache-file (fs/file cache-dir cache-filename)
Expand All @@ -225,19 +264,20 @@
(set post-paths)
(set (fs/modified-since cache-file post-paths)))
_cached-post-paths (set/difference post-paths modified-post-paths)]
(merge (->> cached-posts
(map (fn [[file post]]
[file (assoc post :html (read-cached-post opts file))]))
(into {}))
(->> modified-post-paths
(map (juxt ->filename (partial load-post opts)))
(remove (partial has-error? opts))
(into {}))))
(->> (merge (->> cached-posts
(map (fn [[file post]]
[file (assoc post :html (read-cached-post opts file))]))
(into {}))
(->> modified-post-paths
(map (juxt ->filename (partial load-post opts)))
(remove (partial has-error? opts))
(into {})))
(link-posts opts)))
{}))

(defn only-metadata [posts]
(->> posts
(map (fn [[file post]] [file (dissoc post :html)]))
(map (fn [[file post]] [file (dissoc post :html :prev :next)]))
(into {})))

(defn load-cache [{:keys [cache-dir rendering-system-files]}]
Expand Down
74 changes: 72 additions & 2 deletions test/quickblog/api_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,14 @@
(testing "with blank page suffix"
(with-dirs [posts-dir
templates-dir
out-dir]
out-dir
cache-dir]
(write-test-post posts-dir {:tags #{"foobar" "tag with spaces"}})
(api/render {:page-suffix ""
:posts-dir posts-dir
:templates-dir templates-dir
:out-dir out-dir})
:out-dir out-dir
:cache-dir cache-dir})
(doseq [filename ["test.html" "index.html" "archive.html"
(fs/file "tags" "index.html")
(fs/file "tags" "tag-with-spaces.html")
Expand Down Expand Up @@ -298,6 +300,74 @@
:out-dir out-dir})
(is (not (str/includes? (slurp (fs/file out-dir "test.html")) lib/live-reload-script))))))

(deftest link-posts
(let [posts (->> (range 1 5)
(map (fn [i]
{:file (format "post%s.md" i)
:title (format "post%s" i)
:date (format "2024-02-0%s" i)
:preview? (= 3 i)})))
write-templates! (fn [templates-dir]
(fs/create-dirs templates-dir)
(spit (fs/file templates-dir "base.html")
"{{body | safe }}")
(spit (fs/file templates-dir "post.html")
(str "{% if prev %}prev: {{prev.title}}{% endif %}"
"\n"
"{% if next %}next: {{next.title}}{% endif %}")))]

(testing "add prev and next posts to post metadata"
(with-dirs [posts-dir
templates-dir
cache-dir
out-dir]
(doseq [post posts]
(write-test-post posts-dir post))
(write-templates! templates-dir)
(api/render {:posts-dir posts-dir
:templates-dir templates-dir
:cache-dir cache-dir
:out-dir out-dir})
(is (= (slurp (fs/file out-dir "post1.html"))
"\nnext: post2"))
(is (= (slurp (fs/file out-dir "post2.html"))
"prev: post1\nnext: post4"))
(is (= (slurp (fs/file out-dir "post3.html"))
"\n"))
(is (= (slurp (fs/file out-dir "post4.html"))
"prev: post2\n"))
(let [cache (lib/load-cache {:cache-dir cache-dir})]
(is (nil? (get-in cache ["post1.md" :prev])))
(is (= (get-in cache ["post1.md" :next :title "post2"])))
(is (= (get-in cache ["post2.md" :prev :title "post1"])))
(is (= (get-in cache ["post2.md" :next :title "post4"])))
(is (nil? (get-in cache ["post3.md" :prev])))
(is (nil? (get-in cache ["post3.md" :next])))
(is (= (get-in cache ["post4.md" :prev :title "post2"])))
(is (nil? (get-in cache ["post4.md" :next]))))))

(testing "include preview posts"
(with-dirs [posts-dir
templates-dir
cache-dir
out-dir]
(doseq [post posts]
(write-test-post posts-dir post))
(write-templates! templates-dir)
(api/render {:posts-dir posts-dir
:templates-dir templates-dir
:cache-dir cache-dir
:out-dir out-dir
:include-preview-posts? true})
(is (= (slurp (fs/file out-dir "post1.html"))
"\nnext: post2"))
(is (= (slurp (fs/file out-dir "post2.html"))
"prev: post1\nnext: post3"))
(is (= (slurp (fs/file out-dir "post3.html"))
"prev: post2\nnext: post4"))
(is (= (slurp (fs/file out-dir "post4.html"))
"prev: post3\n"))))))

;; disabled, flaky in CI, cc @jmglov
#_(deftest caching
(testing "assets"
Expand Down
14 changes: 7 additions & 7 deletions test/quickblog/test_runner.clj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
(ns quickblog.test-runner
{:org.babashka/cli {:coerce {:dirs :strings
:nses :symbols
:patterns :strings
:vars :symbols
:includes :keywords
:excludes :keywords
:only :symbols}}}
{:org.babashka/cli {:coerce {:dirs [:string]
:nses [:symbol]
:patterns [:string]
:vars [:symbol]
:includes [:keyword]
:excludes [:keyword]
:only :symbol}}}
(:refer-clojure :exclude [test])
(:require
[clojure.test :as test]
Expand Down
Loading