Index: cache.rkt
==================================================================
--- cache.rkt
+++ cache.rkt
@@ -7,31 +7,26 @@
db/base
db/sqlite3
threading
pollen/setup
racket/match
- (rename-in racket/list
- (group-by group-list-by))
"dust.rkt"
(except-in pollen/core select))
(provide init-cache-db!
cache-conn ; The most eligible bachelor in Neo Yokyo
(schema-out cache:article)
(schema-out cache:note)
- (schema-out cache:series)
(schema-out cache:index-entry)
(schema-out listing)
delete-article!
delete-notes!
articles
articles+notes
listing-htmls
fenced-listing
- unfence
- preheat-series!
- series-grouped-list)
+ unfence)
;; Cache DB and Schemas
(define DBFILE (build-path (current-project-root) "vitreous.sqlite"))
(define cache-conn (make-parameter (sqlite3-connect #:database DBFILE #:mode 'create)))
@@ -47,11 +42,11 @@
[author string/f]
[conceal string/f]
[series-page symbol/f]
[noun-singular string/f]
[note-count integer/f]
- [content-html string/f]
+ [content-html string/f]
[disposition string/f]
[disp-html-anchor string/f]
[listing-full-html string/f] ; full content but without notes
[listing-excerpt-html string/f] ; Not used for now
[listing-short-html string/f])) ; Date and title only
@@ -71,18 +66,10 @@
[conceal string/f]
[listing-full-html string/f]
[listing-excerpt-html string/f] ; Not used for now
[listing-short-html string/f])) ; Date and title only
-(define-schema cache:series #:table "series"
- ([id id/f #:primary-key #:auto-increment]
- [page symbol/f]
- [title string/f]
- [published string/f]
- [noun-plural string/f]
- [noun-singular string/f]))
-
(define-schema cache:index-entry #:table "index_entries"
([id id/f #:primary-key #:auto-increment]
[entry string/f]
[subentry string/f]
[page symbol/f]
@@ -98,11 +85,10 @@
[html string/f]))
(define (init-cache-db!)
(create-table! (cache-conn) 'cache:article)
(create-table! (cache-conn) 'cache:note)
- (create-table! (cache-conn) 'cache:series)
(create-table! (cache-conn) 'cache:index-entry))
(define (delete-article! page)
(query-exec (cache-conn)
(~> (from cache:article #:as a)
@@ -208,32 +194,5 @@
;; Remove "" introduced by using ->html on docs containing output from
;; listing functions
(define (unfence html-str)
(regexp-replace* #px"<[\\/]{0,1}style>" html-str ""))
-
-;;
-;; ~~~ Fetching series ~~~
-;;
-(define (series-grouped-list)
- (~> (for/list ([row (in-entities (cache-conn)
- (order-by (from cache:series #:as s)
- ([s.noun-plural #:asc])))]) row)
- (group-list-by cache:series-noun-plural _ string-ci=?)))
-
-;; Preloads the SQLite cache with info about each series.
-;; I may not actually need this but I’m leaving it for now.
-(define (preheat-series!)
- (query-exec (cache-conn)
- (~> (from cache:series #:as s)
- (where 1)
- delete))
- (define series-rows
- (for/list ([series-pagenode (in-list (cdr (series-pagetree)))])
- (define series-metas (get-metas series-pagenode))
- (make-cache:series
- #:page series-pagenode
- #:title (hash-ref series-metas 'title)
- #:published (hash-ref series-metas 'published "")
- #:noun-plural (hash-ref series-metas 'noun-plural "")
- #:noun-singular (hash-ref series-metas 'noun-singular ""))))
- (void (apply insert! (cache-conn) series-rows)))
Index: code-docs/cache.scrbl
==================================================================
--- code-docs/cache.scrbl
+++ code-docs/cache.scrbl
@@ -42,16 +42,10 @@
in the project root folder) by running queries to create tables in the database if they do not
exist.
}
-@defproc[(preheat-series!) void?]{
-
-Save info about each series in @racket[series-folder] to the cache.
-
-}
-
@section{Retrieving cached data}
Some of this looks a little wacky, but it’s a case of putting a little extra complextity into the
back end to make things simple on the front end. These functions are most commonly used inside the
@emph{body} of a Pollen document (i.e., series pages).
@@ -144,17 +138,10 @@
Use this in templates with strings returned from @racket[->html] when called on docs that use the
@racket[fenced-listing] tag function.
}
-@defproc[(series-grouped-list) (listof (listof cache:series?))]{
-
-Return a list of lists of all @racket[cache:series] in the cache database. The series are grouped so
-that series using the same value in the @tt{noun_plural} column appear together.
-
-}
-
@section{Deleting records}
@deftogether[(@defproc[(delete-article! [page stringish?]) void?]
@defproc[(delete-notes! [page stringish?]) void?])]{
@@ -218,22 +205,10 @@
[listing-short-html string/f])
#:constructor-name make-cache:note]{
Table holding cached information on notes.
-}
-
-@defstruct*[cache:series ([id id/f]
- [page symbol/f]
- [title string/f]
- [published string/f]
- [noun-plural string/f]
- [noun-singular string/f])
- #:constructor-name make-cache:series]{
-
-Table holding cached information on series.
-
}
@defstruct*[cache:index-entry ([id id/f]
[entry string/f]
[subentry string/f]
Index: code-docs/crystalize.scrbl
==================================================================
--- code-docs/crystalize.scrbl
+++ code-docs/crystalize.scrbl
@@ -43,15 +43,6 @@
article. If there are @racket[note]s or @racket[index] tags in the doc, they are parsed and saved
individually to the SQLite cache (using @racket[make-cache:note] and
@racket[make-cache:index-entry]). If any of the notes use the @code{#:disposition} attribute,
information about the disposition is parsed out and used in the rendering of the article.
-}
-
-@defproc[(cache-series!) void?]{
-
-Attempts to look up certain values in @racket[current-metas] which we expect to be defined on
-a typical series page, and saves them to the cache using @racket[make-cache:series]. If @tt{title}
-is not defined in the current metas, you’ll get an error. If any of the others are missing, an empty
-string is used.
-
}
Index: code-docs/dust.scrbl
==================================================================
--- code-docs/dust.scrbl
+++ code-docs/dust.scrbl
@@ -7,10 +7,11 @@
scribble/example)
@(require (for-label "../pollen.rkt"
"../dust.rkt"
"../cache.rkt"
+ "../series-list.rkt"
racket/base
racket/contract
txexpr
sugar/coerce
pollen/tag
@@ -182,24 +183,31 @@
"individual as Queequeg circulating among the polite society of a civilized "
"town, that astonishment soon departed upon taking my first daylight "
"stroll through the streets of New Bedford…")))
(default-title (get-elements doc))]
-@defproc[(metas-series-pagenode) pagenode?]
+@defproc[(current-series-pagenode) pagenode?]
If @code{(current-metas)} has the key @racket['series], converts its value to the pagenode pointing to
that series, otherwise returns @racket['||].
-@defproc[(series-metas-noun) string?]
-
-If @code{(current-metas)} has the key @racket['series], and if the corresponding series defines a meta
-value for @racket['noun-singular], then return it, otherwise return @racket[""].
-
-@defproc[(series-metas-title) string?]
-
-If @code{(current-metas)} has the key @racket['series], and if the corresponding series defines a meta
-value for @racket['title], then return it, otherwise return @racket[""].
+@examples[#:eval dust-eval
+(require pollen/core)
+(parameterize ([current-metas (hash 'series "marquee-fiction")])
+ (current-series-pagenode))]
+
+@defproc[(current-series-noun) string?]
+
+If @code{(current-metas)} has the key @racket['series] and if there is a corresponding
+@racket[series] in the @racket[series-list], return its @racket[series-noun-singular] value;
+otherwise return @racket[""].
+
+@defproc[(current-series-title) string?]
+
+If @code{(current-metas)} has the key @racket['series] and if there is a corresponding
+@racket[series] in the @racket[series-list], return its @racket[series-title] value;
+otherwise return @racket[""].
@defproc[(invalidate-series) (or/c void? boolean?)]
If the current article specifies a @racket['series] meta, and if a corresponding @filepath{.poly.pm}
file exists in @racket[series-folder], attempts to “touch” the last-modified timestamp on that file,
Index: code-docs/main.scrbl
==================================================================
--- code-docs/main.scrbl
+++ code-docs/main.scrbl
@@ -49,10 +49,11 @@
@local-table-of-contents[]
@include-section["tour.scrbl"]
@include-section["design.scrbl"]
@include-section["pollen.scrbl"] @; pollen.rkt
+@include-section["series.scrbl"]
@include-section["dust.scrbl"] @; dust.rkt
@include-section["snippets-html.scrbl"] @; you get the idea
@include-section["cache.scrbl"]
@include-section["crystalize.scrbl"]
@include-section["other-files.scrbl"]
ADDED code-docs/series.scrbl
Index: code-docs/series.scrbl
==================================================================
--- code-docs/series.scrbl
+++ code-docs/series.scrbl
@@ -0,0 +1,74 @@
+#lang scribble/manual
+
+@; SPDX-License-Identifier: BlueOak-1.0.0
+@; This file is licensed under the Blue Oak Model License 1.0.0.
+
+@(require "scribble-helpers.rkt"
+ scribble/example)
+
+@(require (for-label "../pollen.rkt"
+ "../series-list.rkt"
+ "../dust.rkt"
+ "../cache.rkt"
+ pollen/core
+ racket/base
+ racket/contract))
+
+@title{Defining Series}
+
+To create a new series:
+
+@itemlist[#:style 'ordered
+
+ @item{Create a file @filepath{my-key.poly.pm} inside @racket[series-folder] and include a call
+ to @racket[fenced-listing] to list all the articles and notes that will be included in the series:
+ @codeblock|{
+#lang pollen
+
+◊(define-meta title "My New Series")
+
+◊block{Here’s what we call a bunch of similar articles:
+
+◊(fenced-listing (articles 'short))
+
+}
+ }|
+ }
+
+ @item{Add an entry for @racket[_my-key] to @racket[series-list] in @filepath{series-list.rkt}}
+
+ @item{Use @racket[(define-meta series "my-key")] in articles to add them to the series.}
+
+ @item{If @racket[series-ptree-ordered?] is @racket[#t], create a @seclink["Pagetree" #:doc '(lib
+ "pollen/scribblings/pollen.scrbl")]{pagetree} file in @racket[series-folder] named
+ @filepath{my-key.ptree}.}
+
+ ]
+
+@section{Series list}
+
+@defmodule["series-list.rkt" #:packages ()]
+
+This module contains the most commonly used bits of meta-info about @tech{series}. Storing these
+bits in a hash table of structs makes them faster to retrieve than when they are stored inside the
+metas of the Pollen documents for the series themselves.
+
+@defthing[series-list hash?]{
+
+An immutable hash containing all the title and noun info for each @tech{series}. Each key is
+a string and each value is a @racket[series] struct.
+
+}
+
+@defstruct[series ([key string?]
+ [title string?]
+ [noun-plural string?]
+ [noun-singular string?]
+ [ptree-ordered? boolean?])]{
+
+Struct for holding metadata for a @tech{series}. The @racket[_ptree-ordered?] value should be
+@racket[#t] if there is a @filepath{@italic{key}.ptree} file in @racket[series-folder] that provides
+information on how articles in the series are ordered.
+
+}
+
Index: crystalize.rkt
==================================================================
--- crystalize.rkt
+++ crystalize.rkt
@@ -14,12 +14,11 @@
(except-in pollen/core select) ; avoid conflict with deta
)
(require "dust.rkt" "cache.rkt" "snippets-html.rkt")
-(provide parse-and-cache-article!
- cache-series!)
+(provide parse-and-cache-article!)
(define current-title (make-parameter #f))
(define current-excerpt (make-parameter #f))
(define current-notes (make-parameter '()))
(define current-disposition (make-parameter ""))
@@ -53,11 +52,11 @@
(current-disposition)
(current-disp-id))]
[title-html (->html title-tx #:splice? #t)]
[title-plain (tx-strs title-tx)]
[header (html$-article-open pagenode title-specified? title-tx pubdate)]
- [series-node (metas-series-pagenode)]
+ [series-node (current-series-pagenode)]
[footertext (make-article-footertext pagenode
series-node
(current-disposition)
(current-disp-id)
(length (current-notes)))]
@@ -79,11 +78,11 @@
#:published pubdate
#:updated (maybe-meta 'updated)
#:author (maybe-meta 'author default-authorname)
#:conceal (maybe-meta 'conceal)
#:series-page series-node
- #:noun-singular (maybe-meta 'noun (series-metas-noun))
+ #:noun-singular (maybe-meta 'noun (current-series-noun))
#:note-count (length (current-notes))
#:content-html doc-html
#:disposition (current-disposition)
#:disp-html-anchor (current-disp-id)
#:listing-full-html listing-full
@@ -119,14 +118,14 @@
`(title ,@title-elems ,disposition-part))
;; Convert a bunch of information about an article into some nice English and links.
(define (make-article-footertext pagenode series disposition disp-note-id note-count)
(define series-part
- (match (series-metas-title)
+ (match (current-series-title)
[(? non-empty-string? s-title)
(format "This is ~a, part of ‘~a’."
- (series-metas-noun)
+ (current-series-noun)
series
s-title)]
[_ ""]))
(define disp-part
(cond [(non-empty-string? disposition)
@@ -202,11 +201,11 @@
#:title-plain (tx-strs title-tx)
#:published note-date
#:author author
#:author-url author-url
#:disposition disposition-attr
- #:series-page (metas-series-pagenode)
+ #:series-page (current-series-pagenode)
#:conceal (or (maybe-attr 'conceal attrs #f) (maybe-meta 'conceal))
#:content-html content-html
#:listing-full-html listing-full
#:listing-excerpt-html listing-full
#:listing-short-html ""))
@@ -253,22 +252,5 @@
(unless (null? entry-txs)
(void
(apply insert! (cache-conn)
(for/list ([etx (in-list entry-txs)])
(txexpr->index-entry etx pagenode))))))
-
-
-;; Save the current article to the `series` table of the SQLite cache
-;; Should be called from a template for series pages
-(define (cache-series!)
- (define here-page (path->string (here-output-path)))
- (query-exec (cache-conn)
- (delete (~> (from cache:series #:as s)
- (where (= s.page ,here-page)))))
- (void
- (insert-one! (cache-conn)
- (make-cache:series
- #:page (string->symbol here-page)
- #:title (hash-ref (current-metas) 'title)
- #:published (hash-ref (current-metas) 'published "")
- #:noun-plural (hash-ref (current-metas) 'noun-plural "")
- #:noun-singular (hash-ref (current-metas) 'noun-singular "")))))
Index: dust.rkt
==================================================================
--- dust.rkt
+++ dust.rkt
@@ -2,14 +2,16 @@
; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.
(require pollen/core
+ "series-list.rkt"
pollen/pagetree
pollen/setup
pollen/file
net/uri-codec
+ threading
file/sha1
gregor
txexpr
racket/list
racket/match
@@ -23,13 +25,13 @@
maybe-attr ; Return an attribute’s value or a default ("") if not available
here-output-path
here-source-path
here-id
listing-context
- series-metas-noun ; Retrieve noun-singular from current 'series meta, or ""
- series-metas-title ; Retrieve title of series in current 'series meta, or ""
- metas-series-pagenode
+ current-series-noun ; Retrieve noun-singular from current 'series meta, or #f
+ current-series-title ; Retrieve title of series in current 'series meta, or #f
+ current-series-pagenode
invalidate-series
checked-in?
make-tag-predicate
tx-strs
ymd->english
@@ -79,28 +81,30 @@
(define listing-context (make-parameter ""))
;; Checks current-metas for a 'series meta and returns the pagenode of that series,
;; or '|| if no series is specified.
-(define (metas-series-pagenode)
- (define maybe-series (or (select-from-metas 'series (current-metas)) ""))
- (cond
- [(non-empty-string? maybe-series)
- (->pagenode (format "~a/~a.html" series-folder maybe-series))]
- [else '||]))
-
-(define (series-metas-noun)
- (define series-pnode (metas-series-pagenode))
- (case series-pnode
- ['|| ""] ; no series specified
- [else (or (select-from-metas 'noun-singular series-pnode) "")]))
-
-(define (series-metas-title)
- (define series-pnode (metas-series-pagenode))
- (case series-pnode
- ['|| ""] ; no series specified
- [else (or (select-from-metas 'title series-pnode) "")]))
+(define (current-series-pagenode)
+ (or (and~> (current-metas)
+ (hash-ref 'series #f)
+ (format "~a/~a.html" series-folder _)
+ ->pagenode)
+ '||))
+
+(define (current-series-noun)
+ (or (and~> (current-metas)
+ (hash-ref 'series #f)
+ (hash-ref series-list _ #f)
+ series-noun-singular)
+ ""))
+
+(define (current-series-title)
+ (or (and~> (current-metas)
+ (hash-ref 'series #f)
+ (hash-ref series-list _ #f)
+ series-title)
+ ""))
(define article-ids (make-hash))
;; Generates a short ID for the current article
(define (here-id [suffix #f])
Index: makefile
==================================================================
--- makefile
+++ makefile
@@ -5,11 +5,11 @@
# ~~~ Variables used by rules ~~~
#
core-files := pollen.rkt dust.rkt
-html-deps := snippets-html.rkt tags-html.rkt crystalize.rkt cache.rkt
+html-deps := snippets-html.rkt tags-html.rkt crystalize.rkt cache.rkt series-list.rkt
article-sources := $(wildcard articles/*.poly.pm)
articles-html := $(patsubst %.poly.pm, %.html, $(article-sources))
articles-pdf := $(patsubst %.poly.pm, %.pdf, $(article-sources))
Index: pollen.rkt
==================================================================
--- pollen.rkt
+++ pollen.rkt
@@ -31,16 +31,18 @@
(define-runtime-path tags-html.rkt "tags-html.rkt")
(define-runtime-path snippets-html.rkt "snippets-html.rkt")
(define-runtime-path dust.rkt "dust.rkt")
(define-runtime-path crystalize.rkt "crystalize.rkt")
(define-runtime-path cache.rkt "cache.rkt")
+ (define-runtime-path series-list.rkt "series-list.rkt")
(define cache-watchlist
(map resolve-module-path
(list tags-html.rkt
snippets-html.rkt
dust.rkt
cache.rkt
+ series-list.rkt
crystalize.rkt))))
;; Macro for defining tag functions that automatically branch based on the
;; current output format and the list of poly-targets in the setup module.
;; Use this macro when you know you will need keyword arguments.
ADDED series-list.rkt
Index: series-list.rkt
==================================================================
--- series-list.rkt
+++ series-list.rkt
@@ -0,0 +1,33 @@
+#lang racket/base
+
+; SPDX-License-Identifier: BlueOak-1.0.0
+; This file is licensed under the Blue Oak Model License 1.0.0.
+
+;; Provides fast metadata for series.
+;; TO MAKE A NEW SERIES:
+;; 1. Create series/my-key.poly.pm
+;; 2. Add an entry for my-key to series-list below
+;; 3. Use ◊define-meta[series "my-key"] in articles to add them to the series.
+;; 4. If ptree-ordered is #t, also create series/my-key.ptree
+
+(require racket/list)
+(struct series (key title noun-plural noun-singular ptree-ordered?))
+
+(define series-list
+ (make-immutable-hash
+ (list
+ ;; ------- DEFINE SERIES HERE -----------
+ ; Key Title plural noun singular noun phrase
+ (+series "marquee-fiction" "Marquee Fiction" "inventions" "an invention" #f)
+ )))
+
+(define (series-grouped-list)
+ (group-by series-noun-plural (hash-values series-list)))
+
+;; Quick macro to save a little typing
+(define-syntax-rule (+series key title plural singular ptree)
+ (cons key (series key title plural singular ptree)))
+
+(provide (struct-out series)
+ series-list
+ series-grouped-list)
Index: series/template.html.p
==================================================================
--- series/template.html.p
+++ series/template.html.p
@@ -1,8 +1,7 @@
-◊cache-series![]
◊html$-page-head[(select-from-metas 'title metas)]
◊html$-page-body-open["series-page"]
◊(unfence (->html doc #:splice? #t))
Index: snippets-html.rkt
==================================================================
--- snippets-html.rkt
+++ snippets-html.rkt
@@ -10,10 +10,11 @@
racket/string
racket/function
racket/list
txexpr
"cache.rkt"
+ "series-list.rkt"
"dust.rkt")
(provide html$-page-head
html$-page-body-open
html$-series-list
@@ -238,13 +239,13 @@
(page-func (+ pagenum 1) "Older →" "nav-text")))
(string-join `(,prev-link ,@page-group ,next-link)))
(define (series->txpr s)
- `(li (a [[href ,(string-append web-root (symbol->string (cache:series-page s)))]]
- (i ,(cache:series-title s)))))
+ `(li (a [[href ,(string-append web-root (format "~a/~a.html" series-folder (series-key s)))]]
+ (i ,(series-title s)))))
(define (html$-series-list)
(define series-list-items
(for/list ([group (in-list (series-grouped-list))])
- `(div (h2 ,(cache:series-noun-plural (first group))) (ul ,@(map series->txpr group)))))
+ `(div (h2 ,(series-noun-plural (first group))) (ul ,@(map series->txpr group)))))
(->html `(section [[class "column-list"] [style "margin-top: 1.3rem"]] ,@series-list-items)))
Index: util/init.rkt
==================================================================
--- util/init.rkt
+++ util/init.rkt
@@ -7,10 +7,9 @@
(provide main)
(define (main)
(init-cache-db!)
- (preheat-series!)
(display-to-file (html$-page-footer)
(build-path (current-project-root) "scribbled" "site-footer.html")
#:exists 'replace))