Skip to content

Commit 0aec8ae

Browse files
committed
Add Atom/RSS feed generation
1 parent cb387a7 commit 0aec8ae

File tree

8 files changed

+135
-55
lines changed

8 files changed

+135
-55
lines changed

blog/build.rkt

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"paths.rkt"
2121
"build/metadata.rkt"
2222
"build/render/scribble.rkt"
23+
"build/render/feed.rkt"
2324
"build/render/page.rkt")
2425

2526
(define num-posts-per-page 10)
@@ -89,6 +90,10 @@
8990
(display "<!doctype html>" out)
9091
(write-xexpr xexpr out))
9192

93+
(define (write-xml xexpr out)
94+
(display "<?xml version=\"1.0\" encoding=\"utf-8\"?>" out)
95+
(write-xexpr xexpr out))
96+
9297
(define (build-post-body dep)
9398
(define src-mod-time (file-or-directory-modify-seconds (post-dep-src-path dep) #f (λ () #f)))
9499
(define info-mod-time (file-or-directory-modify-seconds (post-dep-info-path dep) #f (λ () #f)))
@@ -132,47 +137,55 @@
132137
out-path
133138
(λ~>> (write-html (post-page info)))))
134139

135-
(define (build-post-index total-pages number posts)
136-
(define site-path (index-path number #:file? #t))
140+
(define (build-index-page total-pages page-number posts #:tag [tag #f])
141+
(define site-path (index-path page-number #:tag tag #:file? #t))
142+
(define out-path (reroot-path site-path output-dir))
137143
(eprintf "~a rendering <output>~a\n" (timestamp-string) site-path)
144+
(make-parent-directory* out-path)
138145
(call-with-output-file* #:exists 'truncate/replace
139-
(reroot-path site-path output-dir)
140-
(λ~>> (write-html (post-index-page total-pages number posts)))))
146+
out-path
147+
(λ~>> (write-html (index-page total-pages page-number posts #:tag tag)))))
148+
149+
(define (build-feeds posts #:tag [tag #f])
150+
(build-feed 'atom posts #:tag tag)
151+
(build-feed 'rss posts #:tag tag))
141152

142-
(define (build-tag-index total-pages page-number tag posts)
143-
(define site-path (tag-index-path tag page-number))
153+
(define (build-feed type posts #:tag tag)
154+
(define site-path (feed-path type #:tag tag))
144155
(define out-path (reroot-path site-path output-dir))
145156
(eprintf "~a rendering <output>~a\n" (timestamp-string) site-path)
146157
(make-parent-directory* out-path)
147158
(call-with-output-file* #:exists 'truncate/replace
148159
out-path
149-
(λ~>> (write-html (tag-index-page total-pages page-number tag posts)))))
160+
(λ~>> (write-xml (feed type posts #:tag tag)))))
150161

151162
(define (build-all)
152163
(make-directory* build-dir)
153164
(make-directory* output-dir)
154-
(define post-infos
165+
(define all-posts
155166
(for/list ([dep (in-list all-post-deps)])
156167
(define info (build-post-body dep))
157168
(build-post-page dep info)
158169
info))
159170

160171
(define total-pages (ceiling (/ (length all-post-deps) num-posts-per-page)))
161-
(for ([posts (in-slice num-posts-per-page (reverse post-infos))]
172+
(for ([posts (in-slice num-posts-per-page (reverse all-posts))]
162173
[number (in-naturals 1)])
163-
(build-post-index total-pages number posts))
174+
(build-index-page total-pages number posts))
175+
(build-feeds (reverse all-posts))
164176

165177
(define tagged-posts
166178
(for*/fold ([tagged-deps+infos (hash)])
167-
([post (in-list post-infos)]
179+
([post (in-list all-posts)]
168180
[tag (in-list (rendered-post-tags post))])
169181
(hash-update tagged-deps+infos tag (λ~>> (cons post)) '())))
170182

171183
(for ([(tag posts) (in-immutable-hash tagged-posts)])
172184
(define total-pages (ceiling (/ (length posts) num-posts-per-page)))
173185
(for ([posts (in-slice num-posts-per-page posts)]
174186
[page-number (in-naturals 1)])
175-
(build-tag-index total-pages page-number tag posts))))
187+
(build-index-page total-pages page-number posts #:tag tag))
188+
(build-feeds posts #:tag tag)))
176189

177190
(module+ main
178191
(build-all))

blog/build/metadata.rkt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
(require racket/contract
44
racket/serialize
5-
(only-in xml xexpr?)
5+
(only-in xml xexpr/c)
66

77
"../paths.rkt"
88
"../lang/metadata.rkt")
@@ -13,10 +13,10 @@
1313

1414
(serializable-struct rendered-post (title-str title date tags body) #:transparent
1515
#:guard (struct-guard/c string?
16-
(listof xexpr?)
16+
(listof xexpr/c)
1717
post-date?
1818
(listof string?)
19-
(listof xexpr?)))
19+
(listof xexpr/c)))
2020

2121
(define (rendered-post-path post #:file? [file? #f])
2222
(post-path (rendered-post-date post)

blog/build/render/feed.rkt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#lang racket/base
2+
3+
(require racket/contract
4+
racket/format
5+
racket/list
6+
racket/match
7+
racket/string
8+
(only-in xml xexpr/c xexpr->string)
9+
10+
"../../lang/metadata.rkt"
11+
"../../paths.rkt"
12+
"../metadata.rkt"
13+
(only-in "page.rkt" index-page-title))
14+
15+
(provide (contract-out
16+
[feed (->* [(or/c 'atom 'rss) (listof rendered-post?)]
17+
[#:tag (or/c string? #f)]
18+
xexpr/c)]))
19+
20+
(define (feed type posts #:tag [tag #f])
21+
(match type
22+
['atom
23+
`(feed ([xmlns "http://www.w3.org/2005/Atom"] [xml:lang "en"])
24+
(title ,(index-page-title #:tag tag))
25+
(link ([rel "self"] [href ,(full-url (feed-path 'atom #:tag tag))]))
26+
(link ([rel "alternate"] [href ,(full-url (index-path #:tag tag))]))
27+
(updated ,(post-date->rfc-3339-datetime (rendered-post-date (first posts))))
28+
,@(for/list ([post (in-list posts)])
29+
(match-define (rendered-post title-str _ date tags body) post)
30+
`(entry
31+
(title ,title-str)
32+
(link ([rel "alternate"]) ,(full-url (rendered-post-path post)))
33+
(published ,(post-date->rfc-3339-datetime date))
34+
(updated ,(post-date->rfc-3339-datetime date))
35+
(author (name "Alexis King"))
36+
(content ([type "html"]) ,(xexpr->string `(article ,@body))))))]
37+
38+
['rss
39+
(define updated (post-date->string (rendered-post-date (first posts))))
40+
`(rss ([version "2.0"])
41+
(channel
42+
(title ,(index-page-title #:tag tag))
43+
(description ,(index-page-title #:tag tag))
44+
(link ,(full-url (index-path #:tag tag)))
45+
(pubDate ,updated)
46+
(lastBuildDate ,updated)
47+
(ttl "60")
48+
,@(for/list ([post (in-list posts)])
49+
(match-define (rendered-post title-str _ date tags body) post)
50+
`(item
51+
(title ,title-str)
52+
(link ,(full-url (rendered-post-path post)))
53+
(guid ([isPermaLink "true"]) ,(full-url (rendered-post-path post)))
54+
(pubDate ,(post-date->string date))
55+
(description ,(xexpr->string `(article ,@body)))))))]))
56+
57+
(define (post-date->rfc-3339-datetime date)
58+
(~a (post-date->string date) "T00:00:00Z"))

blog/build/render/highlight/pygments.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
[close-pygments-server (-> pygments-server? void?)]
1818
[call-with-pygments-server (-> (-> pygments-server? any) any)]
1919
[call-with-current-pygments-server (-> (-> any) any)]
20-
[pygmentize (->* [string? #:language string?] [#:server pygments-server?] xexpr?)]))
20+
[pygmentize (->* [string? #:language string?] [#:server pygments-server?] xexpr/c)]))
2121

2222
(define-runtime-path pygments-server.py "pygments-server.py")
2323

blog/build/render/page.rkt

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,22 @@
66
racket/list
77
racket/match
88
threading
9-
(only-in xml xexpr?)
9+
(only-in xml xexpr/c)
1010

1111
"../../lang/metadata.rkt"
1212
"../../paths.rkt"
13-
"../metadata.rkt")
13+
"../metadata.rkt"
14+
"util.rkt")
1415

1516
(provide (contract-out
16-
[page (-> #:title string? #:body xexpr? xexpr?)]
17-
[post-page (-> rendered-post? xexpr?)]
18-
[post-index-page (->i ([total-pages (and/c exact-integer? (>=/c 1))]
19-
[page-number (total-pages) (and/c exact-integer? (>=/c 1) (<=/c total-pages))]
20-
[posts (listof rendered-post?)])
21-
[result xexpr?])]
22-
[tag-index-page (->i ([total-pages (and/c exact-integer? (>=/c 1))]
23-
[page-number (total-pages) (and/c exact-integer? (>=/c 1) (<=/c total-pages))]
24-
[tag string?]
25-
[posts (listof rendered-post?)])
26-
[result xexpr?])]))
17+
[page (-> #:title string? #:body xexpr/c xexpr/c)]
18+
[post-page (-> rendered-post? xexpr/c)]
19+
[index-page-title (->* [] [#:tag (or/c string? #f)] string?)]
20+
[index-page (->i ([total-pages (and/c exact-integer? (>=/c 1))]
21+
[page-number (total-pages) (and/c exact-integer? (>=/c 1) (<=/c total-pages))]
22+
[posts (listof rendered-post?)])
23+
(#:tag [tag (or/c string? #f)])
24+
[result xexpr/c])]))
2725

2826
(define (page #:title title #:body body)
2927
`(html
@@ -53,8 +51,8 @@
5351
(div "Built with "
5452
(a ([href "https://docs.racket-lang.org/scribble/index.html"]) (strong "Scribble"))
5553
", the Racket document preparation system.")
56-
(div "Feeds are available via " (a ([href "/feeds/all.atom.xml"]) "Atom")
57-
" or " (a ([href "/feeds/all.rss.xml"]) "RSS") "."))))))
54+
(div "Feeds are available via " (a ([href ,(feed-path 'atom)]) "Atom")
55+
" or " (a ([href ,(feed-path 'rss)]) "RSS") "."))))))
5856

5957
(define (post-page info)
6058
(match-define (rendered-post title-str title date tags body) info)
@@ -64,18 +62,18 @@
6462
,(build-post-header title date tags)
6563
,@body))))
6664

67-
(define (post-index-page total-pages page-number posts)
68-
(page #:title "Alexis King’s Blog"
69-
#:body `(div ([class "content"])
70-
,@(build-post-index index-path total-pages page-number posts))))
65+
(define (index-page-title #:tag [tag #f])
66+
(~a (if tag (~a "Posts tagged ‘" tag "’ | ") "")
67+
"Alexis King’s Blog"))
7168

72-
(define (tag-index-page total-pages page-number tag posts)
73-
(page #:title (~a "Posts tagged ‘" tag "")
69+
(define (index-page total-pages page-number posts #:tag [tag #f])
70+
(page #:title (index-page-title #:tag tag)
7471
#:body `(div ([class "content"])
75-
(h1 ([class "tag-page-header"])
76-
"Posts tagged " (em ,tag))
77-
,@(build-post-index (λ~>> (tag-index-path tag))
78-
total-pages page-number posts))))
72+
,@(when/list tag
73+
`(h1 ([class "tag-page-header"])
74+
"Posts tagged " (em ,tag)))
75+
,@(build-post-index (λ~>> (index-path #:tag tag))
76+
total-pages page-number posts))))
7977

8078
(define (build-post-header title date tags)
8179
(define date-str (post-date->string date))
@@ -85,7 +83,7 @@
8583
(time ([datetime ,date-str]) ,date-str)
8684
" " (span ([style "margin: 0 5px"]) "⦿") " "
8785
,@(~> (for/list ([tag (in-list tags)])
88-
`(a ([href ,(tag-index-path tag)]) ,tag))
86+
`(a ([href ,(index-path #:tag tag)]) ,tag))
8987
(add-between ", ")))))
9088

9189
(define (build-post-index page-path total-pages page-number posts)

blog/lang/post-language.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
;; -----------------------------------------------------------------------------
6565

6666
(define (blog-tag tag-str)
67-
(hyperlink (tag-index-path tag-str) tag-str))
67+
(hyperlink (index-path #:tag tag-str) tag-str))
6868

6969
(define-syntax-parser infer-date
7070
[(_) (match (path->string (syntax-source-file-name this-syntax))

blog/markdown/parse.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
(struct unordered-list ([blockss (listof (listof block?))]))
2929
(struct ordered-list ([blockss (listof (listof block?))]))
3030
(struct blockquote ([blocks (listof block?)]))
31-
(struct html-block ([xexpr xexpr?]))
31+
(struct html-block ([xexpr xexpr/c]))
3232

3333
[document/p (parser/c char? document?)]))
3434

blog/paths.rkt

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
[posts-dir path?]
1515

1616
[site-path? predicate/c]
17-
[index-path (->* [(and/c exact-integer? (>=/c 1))] [#:file? any/c] site-path?)]
17+
[index-path (->* [] [(and/c exact-integer? (>=/c 1))
18+
#:tag (or/c string? #f)
19+
#:file? any/c]
20+
site-path?)]
1821
[post-path (->* [post-date? string?] [#:file? any/c] site-path?)]
19-
[tag-index-path (->* [string?] [(and/c exact-integer? (>=/c 1))] site-path?)]))
22+
[feed-path (->* [(or/c 'atom 'rss)] [#:tag (or/c string? #f)] site-path?)]
23+
[full-url (-> site-path? string?)]))
2024

2125
(define-runtime-path build-dir-base "../build")
2226
(define-runtime-path output-dir-base "../output")
@@ -29,17 +33,24 @@
2933
(and (string? v)
3034
(absolute-path? v)))
3135

32-
(define (index-path page-number #:file? [file? #f])
33-
(if (= page-number 1)
34-
(if file? "/index.html" "/")
35-
(~a "/index-" page-number ".html")))
36+
(define (index-path [page-number 1]
37+
#:tag [tag #f]
38+
#:file? [file? #f])
39+
(if tag
40+
(~a "/tags/"
41+
(to-slug tag)
42+
(if (= page-number 1) "" (~a "-" page-number))
43+
".html")
44+
(if (= page-number 1)
45+
(if file? "/index.html" "/")
46+
(~a "/index-" page-number ".html"))))
3647

3748
(define (post-path date title #:file? [file? #f])
3849
(~a "/blog/" (string-join (post-date->strings date) "/")
3950
"/" (to-slug title) "/" (if file? "index.html" "")))
4051

41-
(define (tag-index-path tag-str [page-number 1])
42-
(~a "/tags/"
43-
(string-replace tag-str " " "-")
44-
(if (= page-number 1) "" (~a "-" page-number))
45-
".html"))
52+
(define (feed-path format #:tag [tag #f])
53+
(~a "/feeds/" (if tag (to-slug tag) "all") "." format ".xml"))
54+
55+
(define (full-url path)
56+
(~a "https://lexi-lambda.github.io" path))

0 commit comments

Comments
 (0)