◊(Local Yarn Code "Check-in [3be7a3ab]")

Overview
Comment:Refactor listing functions (see [8ad560204])
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3be7a3ababaccdb34c669285f268a393fdd3427d217a54e70b73a1d13c8c402f
User & Date: joel on 2020-02-20 04:29:24
Other Links: manifest | tags
References
2020-02-20
04:30 Closed ticket [8ad56020]: Refactor listing/query functions plus 3 other changes artifact: 17d1b4f8 user: joel
Context
2020-02-20
04:41
Update code docs for cache to reflect [37240160] check-in: c18bd9b3 user: joel tags: trunk
04:29
Refactor listing functions (see [8ad560204]) check-in: 3be7a3ab user: joel tags: trunk
03:16
Make cache db connection thread-safe check-in: 37240160 user: joel tags: trunk
Changes

Modified cache.rkt from [8c1589ae] to [727011c9].

16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

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

         delete-article!
         delete-notes!
         current-plain-title
         articles
         articles+notes
         listing-htmls
         <listing-full>
         <listing-excerpt>
         <listing-short>
         unfence
         preheat-series!
         series-grouped-list)

;; Cache DB and Schemas

(define DBFILE (build-path (current-project-root) "vitreous.sqlite"))







>






|
<
<







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


31
32
33
34
35
36
37

(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!
         current-plain-title
         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"))
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
;; Get all the a list of the HTML all the results in a query
(define (listing-htmls list-query)
  (for/list ([l (in-entities (cache-conn) list-query)])
    (listing-html l)))

;; Return cached HTML of articles and/or notes, fenced within a style txexpr to prevent it being
;; escaped by ->html. See also: definition of `unfence`

;; E.g.: (<listing-full> articles+notes)
(define (<listing-full> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'full #:series s #:limit lim #:order ord))))
;;                                     ^^^^^

(define (<listing-excerpt> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'excerpt #:series s #:limit lim #:order ord))))
;;                                     ^^^^^^^^

(define (<listing-short> query-func #:series [s #t] #:limit [lim -1] #:order [ord 'desc])
  `(style ,@(listing-htmls (query-func 'short #:series s #:limit lim #:order ord))))
;;                 ^^^^^^

;; 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 ""))

;;







|
<
<
|
<
<
<
<
<
<
<
<
<







181
182
183
184
185
186
187
188


189









190
191
192
193
194
195
196
;; Get all the a list of the HTML all the results in a query
(define (listing-htmls list-query)
  (for/list ([l (in-entities (cache-conn) list-query)])
    (listing-html l)))

;; Return cached HTML of articles and/or notes, fenced within a style txexpr to prevent it being
;; escaped by ->html. See also: definition of `unfence`
(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 [379418fa] to [d41626a2].

39
40
41
42
43
44
45






46
47
48
49
50
51
52
@defproc[(init-cache-db!) void?]{

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.

}







@defparam[current-plain-title title-plain non-empty-string? #:value "void"]{

Contains (or sets) the “plain” title (i.e., with no HTML markup) for the current article based on
analysis done by @racket[parse-and-cache-article!]. If the article did not specify a title,
a default title is supplied. If the article contained a @racket[note] that used the
@code{#:disposition} attribute, the disposition-mark may be included in the title.







>
>
>
>
>
>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@defproc[(init-cache-db!) void?]{

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.

}

@defparam[current-plain-title title-plain non-empty-string? #:value "void"]{

Contains (or sets) the “plain” title (i.e., with no HTML markup) for the current article based on
analysis done by @racket[parse-and-cache-article!]. If the article did not specify a title,
a default title is supplied. If the article contained a @racket[note] that used the
@code{#:disposition} attribute, the disposition-mark may be included in the title.
69
70
71
72
73
74
75
76
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146


147














148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@codeblock|{
#lang pollen

◊title{My New Series}

...some other content

(<listing-excerpt> articles+notes #:order 'asc)
}|
]

@deftogether[(@defproc[(<listing-full> 
                                 [query-func (-> any/c query?)]
                                 [#:series series (or/c string? (listof string?) boolean?) #t]
                                 [#:limit limit integer? -1]
                                 [order stringish? 'desc]) txexpr?]
              @defproc[(<listing-excerpt>
                                 [query-func (-> any/c query?)]
                                 [#:series series (or/c string? (listof string?) boolean?) #t]
                                 [#:limit limit integer? -1]
                                 [order stringish? 'desc]) txexpr?]
              @defproc[(<listing-short>
                                 [query-func (-> any/c query?)]
                                 [#:series series (or/c string? (listof string?) boolean?) #t]
                                 [#:limit limit integer? -1]
                                 [order stringish? 'desc]) txexpr?])]{

Fetches the HTML for items from the SQLite cache, concatenates their HTML strings and returns
a @racket['style] tagged X-expression with this string as its element. The items will be ordered by
publish date according to @racket[_order] and optionally limited to the series specified in
@racket[_series].

The @racket[_query-func] should be either @racket[articles], which will create a listing of articles
only, or @racket[articles+notes], which will include notes intermingled with articles.

@margin-note{Note that the signature shown for the @racket[_query-func] argument above is
incomplete. If you choose to pass a function other than @racket[articles] or
@racket[articles+notes], you must use a function with exactly the same signature as those
functions.}

If @racket[_series] expression evaluates to @racket[#f], articles will not be filtered by series. If
it evaluates to @racket[#t] (the default), articles will be filtered by those that specify the
current output of @racket[here-output-path] in their @tt{series_pagenode} column in the SQLite
cache. If a string is supplied, articles will be filtered by those containing that exact value in
their @tt{series_pagenode} column in the SQLite cache.

The @racket[_order] expression must evaluate to either @racket["ASC"] or @racket["DESC"] and the
@racket[_limit] expressions must evaluate to a value suitable for use in the @tt{LIMIT} clause of
@ext-link["https://sqlite.org/lang_select.html"]{a SQLite @tt{SELECT} statement}. An expression that
evaluates to a negative integer (the default) is the same as having no limit.

The reason for enclosing the results in a @racket['style] txexpr is to prevent the HTML from being
escaped by @racket[->html] in the template. This tag was picked for the job because there will
generally never be a need to include any actual CSS information inside a @tt{<style>} tag in any
page, so it can be safely filtered out later. To remove the enclosing @tt{<style>} tag, see
@racket[unfence].

}

@defproc[(listing-htmls [listing-query query?]) (listof string?)]{

Returns the HTML bodies for the articles and/or notes returned by @racket[_listing-query] as a list
of strings.



}

@deftogether[(@defproc[(articles [type (or/c 'full 'excerpt 'short)]
                                 [#:series series (or/c string? (listof string?) boolean?) #t]
                                 [#:limit limit integer? -1]
                                 [order stringish? 'desc]) query?]
              @defproc[(articles+notes [type (or/c 'full 'excerpt 'short)]
                                       [#:series series (or/c string? (listof string?) boolean?) #t]
                                       [#:limit limit integer? -1]
                                       [order stringish? 'desc]) query?])]{

Create a query that fetches either articles only (@racket[articles]) or articles and notes
intermingled (@racket[articles+notes]), sorted by publish date and optionally limited to
a particular series.

















Typically you will pass these functions by name to listing functions like @racket[<listing-full>]
rather than calling them directly.

@examples[#:eval example-eval
(articles 'full)]
}

@defproc[(unfence [html string?]) string?]{

Returns @racket[_html] with all occurrences of @racket["<style>"] and @racket["</style>"] removed.
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[<listing-full>] tag function or its siblings.

}

@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?])]{







|



<
<
<
<
<
<
<
<
<
<
|
<
<
<
<

<
|
<
<
|
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<












|
>
>












|
|
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
|












|






|







75
76
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@codeblock|{
#lang pollen

◊title{My New Series}

...some other content

fenced-listing[(articles+notes 'excerpt #:order 'asc)]
}|
]











@defproc[(fenced-listing [query query?]) txexpr?]{






Fetches a the HTML strings from the SQLite cache and returns a @racket['style] tagged X-expression


with these strings as its elements. The @racket[_query] will usually be the result of a call to


@racket[articles] or @racket[articles+notes], but can be any custom query that projects onto the




@racket[listing] schema (see @racket[project-onto]).











The reason for enclosing the results in a @racket['style] txexpr is to prevent the HTML from being
escaped by @racket[->html] in the template. This tag was picked for the job because there will
generally never be a need to include any actual CSS information inside a @tt{<style>} tag in any
page, so it can be safely filtered out later. To remove the enclosing @tt{<style>} tag, see
@racket[unfence].

}

@defproc[(listing-htmls [listing-query query?]) (listof string?)]{

Returns the HTML bodies for the articles and/or notes returned by @racket[_listing-query] as a list
of strings. The @racket[_query] will usually be the result of a call to @racket[articles] or
@racket[articles+notes], but can be any custom query that projects onto the @racket[listing] schema
(see @racket[project-onto]).

}

@deftogether[(@defproc[(articles [type (or/c 'full 'excerpt 'short)]
                                 [#:series series (or/c string? (listof string?) boolean?) #t]
                                 [#:limit limit integer? -1]
                                 [order stringish? 'desc]) query?]
              @defproc[(articles+notes [type (or/c 'full 'excerpt 'short)]
                                       [#:series series (or/c string? (listof string?) boolean?) #t]
                                       [#:limit limit integer? -1]
                                       [order stringish? 'desc]) query?])]{

Create a query that fetches either articles only, or articles and notes intermingled, respectively.
The results will be sorted by publish date according to @racket[_order] and optionally limited to
a particular series. Use the resulting query with the @racket[listing-htmls] or
@racket[fenced-listing] functions provided by this module, or with deta’s @racket[in-entities] if
you want to work with the @racket[listing] schema structs.

If @racket[_series] expression evaluates to @racket[#f], articles will not be filtered by series. If
it evaluates to @racket[#t] (the default), articles will be filtered by those that specify the
current output of @racket[here-output-path] in their @tt{series_pagenode} column in the SQLite
cache. If a string or a symbol is supplied, articles will be filtered by those containing the result
of @racket[(format "series/~a.html" _series)] in their @tt{series_pagenode} column in the SQLite
cache. If a list of strings or symbols is provided, this @racket[format] operation will be applied
to each of its members and articles whose @tt{series_pagenode} column matches any of the resulting
values will be included.

The @racket[_order] expression must evaluate to either @racket["ASC"] or @racket["DESC"] and the
@racket[_limit] expressions must evaluate to a value suitable for use in the @tt{LIMIT} clause of
@ext-link["https://sqlite.org/lang_select.html"]{a SQLite @tt{SELECT} statement}. An expression that
evaluates to a negative integer (the default) is the same as having no limit.

Typically you will pass these functions by name to listing functions like @racket[fenced-listing]
rather than calling them directly.

@examples[#:eval example-eval
(articles 'full)]
}

@defproc[(unfence [html string?]) string?]{

Returns @racket[_html] with all occurrences of @racket["<style>"] and @racket["</style>"] removed.
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?])]{

Modified code-docs/pollen.scrbl from [3b0baad7] to [885fb15f].

77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93

}

@defproc[(block [element xexpr?] ...) txexpr?]{

A container for content that should appear grouped together on larger displays. Intended for use in
Series pages, where the template is very minimal to allow for more customization. You would want
output from @racket[<listing-short>] to appear inside a @racket[block], but you would want output

from @racket[<listing-full>] to appear outside it (since each article effectively supplies its own
block). Only relevant to HTML output.

}

@deftogether[(@defproc[(link [link-id stringish?] [link-text xexpr?]) txexpr?]
              @defproc[(url  [link-id stringish?] [url string?]) void?])]{

All hyperlinks are specified reference-style. So, to link some text, use the @code{link} tag with







|
>
|
|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

}

@defproc[(block [element xexpr?] ...) txexpr?]{

A container for content that should appear grouped together on larger displays. Intended for use in
Series pages, where the template is very minimal to allow for more customization. You would want
output from @racket[(fenced-listing (articles 'short))] to appear inside a @racket[block], but when
using @racket['excerpt] or @racket['full] in place of @racket['short] in that code, you would want
the output to appear outside it (since the “full” and “excerpt” versions of each article effectively
supply their own blocks). Only relevant to HTML output.

}

@deftogether[(@defproc[(link [link-id stringish?] [link-text xexpr?]) txexpr?]
              @defproc[(url  [link-id stringish?] [url string?]) void?])]{

All hyperlinks are specified reference-style. So, to link some text, use the @code{link} tag with