◊(Local Yarn Code "Check-in [71cdd100]")

Overview
Comment:Change system for managing series: remove from SQLite cache, manage as a hash table of structs instead.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 71cdd10072602a9888fdd94c9eef42e582fc1c4b4ec888850f9c349a53825eb2
User & Date: joel on 2020-03-15 18:33:12
Other Links: manifest | tags
Context
2020-03-15
20:47
Tighten up the home page check-in: f2c37b22 user: joel tags: trunk
18:33
Change system for managing series: remove from SQLite cache, manage as a hash table of structs instead. check-in: 71cdd100 user: joel tags: trunk
18:27
Fix copyright, link in ‘Future Proofing’ check-in: 078a4e03 user: joel tags: trunk, errata
Changes

Modified cache.rkt from [cd0a6087] to [22c08e42].

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

(require deta
         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)

;; Cache DB and Schemas

(define DBFILE (build-path (current-project-root) "vitreous.sqlite"))
(define cache-conn (make-parameter (sqlite3-connect #:database DBFILE #:mode 'create)))

(define-schema cache:article #:table "articles"
................................................................................
   [published            string/f]
   [updated              string/f]
   [author               string/f]
   [conceal              string/f]
   [series-page          symbol/f]
   [noun-singular        string/f]
   [note-count           integer/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

(define-schema cache:note #:table "notes"
................................................................................
   [content-html         string/f]
   [series-page          symbol/f]
   [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]
   [html-anchor string/f]))

................................................................................
   [published   string/f]
   [updated     string/f]
   [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)
                  (where (= a.page ,(format "~a" page)))
                  delete)))
................................................................................
(define (fenced-listing q)
  `(style ,@(listing-htmls q)))

;; Remove "<style>" and "</style>" 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)))







<
<







<








|
<
<







 







|







 







<
<
<
<
<
<
<
<







 







<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
5
6
7
8
9
10
11


12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27


28
29
30
31
32
33
34
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
..
64
65
66
67
68
69
70








71
72
73
74
75
76
77
..
83
84
85
86
87
88
89

90
91
92
93
94
95
96
...
192
193
194
195
196
197
198




























(require deta
         db/base
         db/sqlite3
         threading
         pollen/setup
         racket/match


         "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:index-entry)
         (schema-out listing)
         delete-article!
         delete-notes!
         articles
         articles+notes
         listing-htmls
         fenced-listing
         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)))

(define-schema cache:article #:table "articles"
................................................................................
   [published            string/f]
   [updated              string/f]
   [author               string/f]
   [conceal              string/f]
   [series-page          symbol/f]
   [noun-singular        string/f]
   [note-count           integer/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

(define-schema cache:note #:table "notes"
................................................................................
   [content-html         string/f]
   [series-page          symbol/f]
   [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:index-entry #:table "index_entries"
  ([id          id/f #:primary-key #:auto-increment]
   [entry       string/f]
   [subentry    string/f]
   [page        symbol/f]
   [html-anchor string/f]))

................................................................................
   [published   string/f]
   [updated     string/f]
   [html        string/f]))

(define (init-cache-db!)
  (create-table! (cache-conn) 'cache:article)
  (create-table! (cache-conn) 'cache:note)

  (create-table! (cache-conn) 'cache:index-entry))

(define (delete-article! page)
  (query-exec (cache-conn)
              (~> (from cache:article #:as a)
                  (where (= a.page ,(format "~a" page)))
                  delete)))
................................................................................
(define (fenced-listing q)
  `(style ,@(listing-htmls q)))

;; Remove "<style>" and "</style>" introduced by using ->html on docs containing output from
;; listing functions
(define (unfence html-str)
  (regexp-replace* #px"<[\\/]{0,1}style>" html-str ""))



























Modified code-docs/cache.scrbl from [bc87f6f5] to [ed5e44d9].

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

Creates and initializes the SQLite database cache file (named @filepath{vitreous.sqlite} and located
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). 

@filebox["series/my-series.poly.pm"
................................................................................
The contents of the style tags are left intact.

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?])]{
              
Delete a particular article, or all notes for a particular article, respectively.

................................................................................
                        [listing-full-html    string/f]
                        [listing-excerpt-html string/f]
                        [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]
                               [page        symbol/f]
                               [html-anchor string/f])







<
<
<
<
<
<







 







<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<







40
41
42
43
44
45
46






47
48
49
50
51
52
53
...
136
137
138
139
140
141
142







143
144
145
146
147
148
149
...
203
204
205
206
207
208
209












210
211
212
213
214
215
216

Creates and initializes the SQLite database cache file (named @filepath{vitreous.sqlite} and located
in the project root folder) by running queries to create tables in the database if they do not
exist.

}







@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). 

@filebox["series/my-series.poly.pm"
................................................................................
The contents of the style tags are left intact.

Use this in templates with strings returned from @racket[->html] when called on docs that use the
@racket[fenced-listing] tag function.

}








@section{Deleting records}

@deftogether[(@defproc[(delete-article! [page stringish?]) void?]
              @defproc[(delete-notes!   [page stringish?]) void?])]{
              
Delete a particular article, or all notes for a particular article, respectively.

................................................................................
                        [listing-full-html    string/f]
                        [listing-excerpt-html string/f]
                        [listing-short-html   string/f])
                       #:constructor-name make-cache:note]{

Table holding cached information on notes.













}

@defstruct*[cache:index-entry ([id id/f]
                               [entry       string/f]
                               [subentry    string/f]
                               [page        symbol/f]
                               [html-anchor string/f])

Modified code-docs/crystalize.scrbl from [ca8a0906] to [d002cd00].

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
and saved to the SQLite cache and saved using @racket[make-cache:article]. If the article specifies
a @racket['series] meta, information about that series is fetched and used in the rendering of the
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.

}








<
<
<
<
<
<
<
<
<
41
42
43
44
45
46
47
48









and saved to the SQLite cache and saved using @racket[make-cache:article]. If the article specifies
a @racket['series] meta, information about that series is fetched and used in the rendering of the
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. 

}









Modified code-docs/dust.scrbl from [ed5349c7] to [6b21bda7].

5
6
7
8
9
10
11

12
13
14
15
16
17
18
...
180
181
182
183
184
185
186
187
188
189
190
191
192



193


194

195
196
197

198
199

200
201
202
203
204
205
206
207

@(require "scribble-helpers.rkt"
          scribble/example)

@(require (for-label "../pollen.rkt"
                     "../dust.rkt"
                     "../cache.rkt"

                     racket/base
                     racket/contract
                     txexpr
                     sugar/coerce
                     pollen/tag
                     pollen/setup
                     pollen/pagetree
................................................................................
(define doc 
  '(root (p "If I had been astonished at first catching a glimpse of so outlandish an "
            "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?]

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[""].

@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,
returning @racket[#t] on success or @racket[#f] on failure. If either precondition is not true,
returns @|void-const|.







>







 







|




|
>
>
>

>
>
|
>
|

<
>

|
>
|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211
212
213
214
215

@(require "scribble-helpers.rkt"
          scribble/example)

@(require (for-label "../pollen.rkt"
                     "../dust.rkt"
                     "../cache.rkt"
                     "../series-list.rkt"
                     racket/base
                     racket/contract
                     txexpr
                     sugar/coerce
                     pollen/tag
                     pollen/setup
                     pollen/pagetree
................................................................................
(define doc 
  '(root (p "If I had been astonished at first catching a glimpse of so outlandish an "
            "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[(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['||].

@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,
returning @racket[#t] on success or @racket[#f] on failure. If either precondition is not true,
returns @|void-const|.

Modified code-docs/main.scrbl from [d030a01e] to [1cad1ba1].

47
48
49
50
51
52
53

54
55
56
57
58
cached metas of every article looking for matching articles.

@local-table-of-contents[]

@include-section["tour.scrbl"]
@include-section["design.scrbl"]
@include-section["pollen.scrbl"]  @; pollen.rkt

@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"]







>





47
48
49
50
51
52
53
54
55
56
57
58
59
cached metas of every article looking for matching articles.

@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 version [09dae3f6].





















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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.

}

Modified crystalize.rkt from [121eb65c] to [7aa92528].

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
         pollen/template
         pollen/decode
         (except-in pollen/core select) ; avoid conflict with deta
         )

(require "dust.rkt" "cache.rkt" "snippets-html.rkt")

(provide parse-and-cache-article!
         cache-series!)

(define current-title       (make-parameter #f))
(define current-excerpt     (make-parameter #f))
(define current-notes       (make-parameter '()))
(define current-disposition (make-parameter ""))
(define current-disp-id     (make-parameter ""))

................................................................................
                                          title-val
                                          body-txpr
                                          (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)]
         [footertext  (make-article-footertext pagenode
                                               series-node
                                               (current-disposition)
                                               (current-disp-id)
                                               (length (current-notes)))]
         [footer (html$-article-close footertext)]
         [listing-short (html$-article-listing-short pagenode pubdate title-html)]
................................................................................
                  #:title-html-flow title-html
                  #:title-specified? title-specified?
                  #: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))
                  #:note-count (length (current-notes))
                  #:content-html doc-html
                  #:disposition (current-disposition)
                  #:disp-html-anchor (current-disp-id)
                  #:listing-full-html listing-full
                  #:listing-excerpt-html listing-excerpt
                  #:listing-short-html listing-short))
................................................................................
          [else ""]))
  ;; Returns a txexpr, the tag will be discarded by the template/snippets
  `(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)
      [(? non-empty-string? s-title)
       (format "<span class=\"series-part\">This is ~a, part of <a href=\"/~a\">‘~a’</a>.</span>"
               (series-metas-noun)
               series
               s-title)]
      [_ ""]))
  (define disp-part
    (cond [(non-empty-string? disposition)
           (define-values (mark verb) (disposition-values disposition))
           (format "Now considered <a href=\"/~a#~a\">~a</a>."
................................................................................
                  #:html-anchor note-id
                  #:title-html-flow title-html
                  #:title-plain (tx-strs title-tx)
                  #:published note-date
                  #:author author
                  #:author-url author-url
                  #:disposition disposition-attr
                  #:series-page (metas-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 ""))
    (html$-note-in-article note-id note-date content-html author author-url)))

................................................................................
  (query-exec (cache-conn) (delete (~> (from cache:index-entry #:as entry)
                                       (where (= entry.page ,(symbol->string pagenode))))))
  (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 "")))))







|
<







 







|







 







|







 







|


|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
250
251
252
253
254
255
256

















         pollen/template
         pollen/decode
         (except-in pollen/core select) ; avoid conflict with deta
         )

(require "dust.rkt" "cache.rkt" "snippets-html.rkt")

(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 ""))
(define current-disp-id     (make-parameter ""))

................................................................................
                                          title-val
                                          body-txpr
                                          (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 (current-series-pagenode)]
         [footertext  (make-article-footertext pagenode
                                               series-node
                                               (current-disposition)
                                               (current-disp-id)
                                               (length (current-notes)))]
         [footer (html$-article-close footertext)]
         [listing-short (html$-article-listing-short pagenode pubdate title-html)]
................................................................................
                  #:title-html-flow title-html
                  #:title-specified? title-specified?
                  #: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 (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
                  #:listing-excerpt-html listing-excerpt
                  #:listing-short-html listing-short))
................................................................................
          [else ""]))
  ;; Returns a txexpr, the tag will be discarded by the template/snippets
  `(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 (current-series-title)
      [(? non-empty-string? s-title)
       (format "<span class=\"series-part\">This is ~a, part of <a href=\"/~a\">‘~a’</a>.</span>"
               (current-series-noun)
               series
               s-title)]
      [_ ""]))
  (define disp-part
    (cond [(non-empty-string? disposition)
           (define-values (mark verb) (disposition-values disposition))
           (format "Now considered <a href=\"/~a#~a\">~a</a>."
................................................................................
                  #:html-anchor note-id
                  #:title-html-flow title-html
                  #:title-plain (tx-strs title-tx)
                  #:published note-date
                  #:author author
                  #:author-url author-url
                  #:disposition disposition-attr
                  #: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 ""))
    (html$-note-in-article note-id note-date content-html author author-url)))

................................................................................
  (query-exec (cache-conn) (delete (~> (from cache:index-entry #:as entry)
                                       (where (= entry.page ,(symbol->string pagenode))))))
  (unless (null? entry-txs)
    (void
     (apply insert! (cache-conn)
            (for/list ([etx (in-list entry-txs)])
              (txexpr->index-entry etx pagenode))))))

















Modified dust.rkt from [eb603279] to [aa082dfb].

1
2
3
4
5
6

7
8
9
10

11
12
13
14
15
16
17
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95

96
97
98
99
100
101


102
103
104
105
106
107
108
#lang racket/base

; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.

(require pollen/core

         pollen/pagetree
         pollen/setup
         pollen/file
         net/uri-codec

         file/sha1
         gregor
         txexpr
         racket/list
         racket/match
         racket/port
         racket/system
................................................................................

(provide maybe-meta     ; Select from (current-metas) or default value ("") if not available
         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
         invalidate-series
         checked-in?
         make-tag-predicate
         tx-strs
         ymd->english
         ymd->dateformat
         default-authorname
................................................................................
(define (here-output-path)
  (->output-path (here-source-path)))

(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 article-ids (make-hash))

;; Generates a short ID for the current article
(define (here-id [suffix #f])
  (define maybe-hash (hash-ref article-ids (here-output-path) #f))
  (define here-hash






>




>







 







|
|
|







 







|
|
|
<
|
>
|

|
|
|
|
|
>

<
|
|
|
|
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
#lang racket/base

; 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
         racket/port
         racket/system
................................................................................

(provide maybe-meta     ; Select from (current-metas) or default value ("") if not available
         maybe-attr     ; Return an attribute’s value or a default ("") if not available
         here-output-path
         here-source-path
         here-id
         listing-context
         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
         ymd->dateformat
         default-authorname
................................................................................
(define (here-output-path)
  (->output-path (here-source-path)))

(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 (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])
  (define maybe-hash (hash-ref article-ids (here-output-path) #f))
  (define here-hash

Modified makefile from [fae17d31] to [7107d1ff].

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

SHELL = /bin/bash

# ~~~ Variables used by rules ~~~
#

core-files := pollen.rkt dust.rkt
html-deps  := snippets-html.rkt tags-html.rkt crystalize.rkt cache.rkt

article-sources := $(wildcard articles/*.poly.pm)
articles-html   := $(patsubst %.poly.pm, %.html, $(article-sources))
articles-pdf    := $(patsubst %.poly.pm, %.pdf,  $(article-sources))

series-sources  := $(wildcard series/*.poly.pm)
series-html     := $(patsubst %.poly.pm, %.html, $(series-sources))







|







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

SHELL = /bin/bash

# ~~~ Variables used by rules ~~~
#

core-files := pollen.rkt dust.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))

series-sources  := $(wildcard series/*.poly.pm)
series-html     := $(patsubst %.poly.pm, %.html, $(series-sources))

Modified pollen.rkt from [ecc1875a] to [0c603aac].

29
30
31
32
33
34
35

36
37
38
39
40
41

42
43
44
45
46
47
48
  (define block-tags (append '(title style dt note) default-block-tags))

  (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 cache-watchlist
    (map resolve-module-path
         (list tags-html.rkt
               snippets-html.rkt
               dust.rkt
               cache.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.
;;
(define-syntax (poly-branch-kwargs-tag stx)







>






>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  (define block-tags (append '(title style dt note) default-block-tags))

  (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.
;;
(define-syntax (poly-branch-kwargs-tag stx)

Added series-list.rkt version [88c1de55].



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)

Modified series/template.html.p from [7a674800] to [7e883695].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
◊cache-series![]
◊html$-page-head[(select-from-metas 'title metas)]

◊html$-page-body-open["series-page"]

◊(unfence (->html doc #:splice? #t))

◊html$-page-body-close[]

</html>




<











1
2

3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">

◊html$-page-head[(select-from-metas 'title metas)]

◊html$-page-body-open["series-page"]

◊(unfence (->html doc #:splice? #t))

◊html$-page-body-close[]

</html>


Modified snippets-html.rkt from [79ce1a10] to [9c1944e9].

8
9
10
11
12
13
14

15
16
17
18
19
20
21
...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
         pollen/decode
         pollen/private/version
         racket/string
         racket/function
         racket/list
         txexpr
         "cache.rkt"

         "dust.rkt")

(provide html$-page-head
         html$-page-body-open
         html$-series-list
         html$-article-open
         html$-article-close
................................................................................
    (if (eq? pagecount pagenum)
        "<li class=\"nav-text inactive-link\">Older&rarr;</li>"
        (page-func (+ pagenum 1) "Older&thinsp;&rarr;" "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)))))

(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)))))
  (->html `(section [[class "column-list"] [style "margin-top: 1.3rem"]] ,@series-list-items)))







>







 







|
|




|

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
         pollen/decode
         pollen/private/version
         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
         html$-article-open
         html$-article-close
................................................................................
    (if (eq? pagecount pagenum)
        "<li class=\"nav-text inactive-link\">Older&rarr;</li>"
        (page-func (+ pagenum 1) "Older&thinsp;&rarr;" "nav-text")))

  (string-join `(,prev-link ,@page-group ,next-link)))

(define (series->txpr 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 ,(series-noun-plural (first group))) (ul ,@(map series->txpr group)))))
  (->html `(section [[class "column-list"] [style "margin-top: 1.3rem"]] ,@series-list-items)))

Modified util/init.rkt from [41c57f81] to [ae86fdec].

5
6
7
8
9
10
11
12
13
14
15
16
         "../cache.rkt"
         "../snippets-html.rkt")

(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))







<




5
6
7
8
9
10
11

12
13
14
15
         "../cache.rkt"
         "../snippets-html.rkt")

(provide main)

(define (main)
  (init-cache-db!)

  
  (display-to-file (html$-page-footer)
                   (build-path (current-project-root) "scribbled" "site-footer.html")
                   #:exists 'replace))