Make it work by using dictionaries in template context 👎

This is a work-around but it works.
This commit is contained in:
Sami Samhuri 2019-12-04 17:19:08 -08:00
parent 57de420eee
commit 4a03060c8c
6 changed files with 87 additions and 43 deletions

View file

@ -7,7 +7,7 @@
import Foundation import Foundation
struct Month { struct Month: Equatable {
static let names = [ static let names = [
"January", "Februrary", "March", "April", "January", "Februrary", "March", "April",
"May", "June", "July", "August", "May", "June", "July", "August",
@ -16,15 +16,49 @@ struct Month {
let number: Int let number: Int
init(_ number: Int) {
precondition((1 ... 12).contains(number), "Month number must be from 1 to 12, got \(number)")
self.number = number
}
init(_ name: String) {
precondition(Month.names.contains(name), "Month name is unknown: \(name)")
self.number = 1 + Month.names.firstIndex(of: name)!
}
var padded: String { var padded: String {
String(format: "%02d", number) String(format: "%02d", number)
} }
var name: String { var name: String {
Month.names[number] Month.names[number - 1]
} }
var abbreviatedName: String { var abbreviatedName: String {
String(name.prefix(3)) String(name.prefix(3))
} }
} }
extension Month: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(number)
}
}
extension Month: Comparable {
static func <(lhs: Month, rhs: Month) -> Bool {
lhs.number < rhs.number
}
}
extension Month: ExpressibleByIntegerLiteral {
init(integerLiteral value: Int) {
self.init(value)
}
}
extension Month: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
self.init(value)
}
}

View file

@ -8,7 +8,7 @@
import Foundation import Foundation
struct MonthPosts { struct MonthPosts {
let month: Int let month: Month
var posts: [Post] var posts: [Post]
var isEmpty: Bool { var isEmpty: Bool {
@ -18,9 +18,9 @@ struct MonthPosts {
struct YearPosts { struct YearPosts {
let year: Int let year: Int
var byMonth: [Int: MonthPosts] var byMonth: [Month: MonthPosts]
subscript(month: Int) -> MonthPosts { subscript(month: Month) -> MonthPosts {
get { get {
byMonth[month, default: MonthPosts(month: month, posts: [])] byMonth[month, default: MonthPosts(month: month, posts: [])]
} }
@ -56,7 +56,7 @@ struct PostsByYear {
} }
mutating func add(post: Post) { mutating func add(post: Post) {
let (year, month) = (post.date.year, post.date.month) let (year, month) = (post.date.year, Month(post.date.month))
self[year][month].posts.append(post) self[year][month].posts.append(post)
} }

View file

@ -29,18 +29,16 @@ final class PostsPlugin: Plugin {
let posts = try enumerateMarkdownFiles(directory: postsURL) let posts = try enumerateMarkdownFiles(directory: postsURL)
.compactMap { (url: URL) -> Post? in .compactMap { (url: URL) -> Post? in
guard let result = (try? String(contentsOf: url)).map(markdownParser.parse) else {
return nil
}
do { do {
return try Post(bodyMarkdown: "(TEST)", metadata: result.metadata) let markdown = try String(contentsOf: url)
let result = markdownParser.parse(markdown)
return try Post(bodyMarkdown: result.html, metadata: result.metadata)
} }
catch { catch {
print("Cannot create post from markdown file \(url): \(error)") print("Cannot create post from markdown file \(url): \(error)")
return nil return nil
} }
} }
print("posts: \(posts)")
self.posts = PostsByYear(posts: posts) self.posts = PostsByYear(posts: posts)
} }
@ -76,12 +74,12 @@ final class PostsPlugin: Plugin {
func renderYearsAndMonths(postsDir: URL, templateRenderer: TemplateRenderer) throws { func renderYearsAndMonths(postsDir: URL, templateRenderer: TemplateRenderer) throws {
print("renderYearsAndMonths(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") print("renderYearsAndMonths(postsDir: \(postsDir), templateRenderer: \(templateRenderer)")
let allMonths = (1 ... 12).reversed().map(Month.init) let allMonths = (1 ... 12).map(Month.init(_:))
for (year, monthPosts) in posts.byYear.sorted(by: { $1.key < $0.key }) { for (year, monthPosts) in posts.byYear.sorted(by: { $1.key < $0.key }) {
let yearDir = postsDir.appendingPathComponent("\(year)") let yearDir = postsDir.appendingPathComponent("\(year)")
var sortedPostsByMonth: [Int: [RenderedPost]] = [:] var sortedPostsByMonth: [Month: [RenderedPost]] = [:]
for month in allMonths { for month in allMonths {
let sortedPosts = monthPosts[month.number].posts.sorted(by: { $1.date < $0.date }) let sortedPosts = monthPosts[month].posts.sorted(by: { $1.date < $0.date })
guard !sortedPosts.isEmpty else { guard !sortedPosts.isEmpty else {
continue continue
} }
@ -90,7 +88,7 @@ final class PostsPlugin: Plugin {
let bodyHTML = markdownParser.html(from: post.bodyMarkdown) let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
return RenderedPost(post: post, body: bodyHTML) return RenderedPost(post: post, body: bodyHTML)
} }
sortedPostsByMonth[month.number] = renderedPosts sortedPostsByMonth[month] = renderedPosts
let monthDir = yearDir.appendingPathComponent(month.padded) let monthDir = yearDir.appendingPathComponent(month.padded)
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
@ -101,11 +99,23 @@ final class PostsPlugin: Plugin {
} }
try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil) try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil)
let months = Array(sortedPostsByMonth.keys.sorted().reversed())
let postsByMonthForContext: [String: [[String: Any]]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in
let (month, renderedPosts) = pair
dict[month.padded] = renderedPosts.map { $0.dictionary }
}
let monthsPadded = months.map { $0.padded }
let context: [String: Any] = [ let context: [String: Any] = [
"path": path, "path": path,
"year": year, "year": year,
"months": sortedPostsByMonth.keys.sorted().reversed().map(Month.init), "months": monthsPadded,
"postsByMonth": sortedPostsByMonth, "monthNames": months.reduce(into: [String: String](), { dict, month in
dict[month.padded] = month.name
}),
"monthAbbreviations": months.reduce(into: [String: String](), { dict, month in
dict[month.padded] = month.abbreviatedName
}),
"postsByMonth": postsByMonthForContext,
] ]
let yearHTML = try templateRenderer.renderTemplate(name: "posts-year", context: context) let yearHTML = try templateRenderer.renderTemplate(name: "posts-year", context: context)
let yearURL = yearDir.appendingPathComponent("index.html") let yearURL = yearDir.appendingPathComponent("index.html")
@ -121,7 +131,7 @@ final class PostsPlugin: Plugin {
let templateName = self.templateName(for: post) let templateName = self.templateName(for: post)
let bodyHTML = markdownParser.html(from: post.bodyMarkdown) let bodyHTML = markdownParser.html(from: post.bodyMarkdown)
let renderedPost = RenderedPost(post: post, body: bodyHTML) let renderedPost = RenderedPost(post: post, body: bodyHTML)
let postHTML = try templateRenderer.renderTemplate(name: templateName, context: ["post": renderedPost]) let postHTML = try templateRenderer.renderTemplate(name: templateName, context: ["post": renderedPost.dictionary])
try postHTML.write(to: postURL, atomically: true, encoding: .utf8) try postHTML.write(to: postURL, atomically: true, encoding: .utf8)
} }

View file

@ -11,17 +11,17 @@ struct RenderedPost {
let post: Post let post: Post
let body: String let body: String
var author: String { post.author } var dictionary: [String: Any] {
[
var title: String { post.title } "author": post.author,
"title": post.title,
var date: Date { post.date } "date": post.date,
"day": post.date.day,
var formattedDate: String { post.formattedDate } "formattedDate": post.formattedDate,
"isLink": post.isLink,
var isLink: Bool { post.isLink } "link": post.link as Any,
"path": post.path,
var link: URL? { post.link } "body": body,
]
var path: String { post.path } }
} }

View file

@ -3,7 +3,7 @@
{% block body %} {% block body %}
<article class="container"> <article class="container">
<header> <header>
<h1><a href="{{ post.url }}">{{ post.title }}</a></h1> <h1><a href="{{ post.path }}">{{ post.title }}</a></h1>
<time>{{ post.formattedDate }}</time> <time>{{ post.formattedDate }}</time>
</header> </header>
{{ post.body }} {{ post.body }}

View file

@ -4,24 +4,24 @@
<div class="container"> <div class="container">
<h2>{{ year }}</h2> <h2>{{ year }}</h2>
{% for month in months %} {% for month in months %}
<h3> <h3>
<a href="/{{ path }}/{{ year }}/{{ month.padded }}">{{ month.name }}</a> <a href="/{{ path }}/{{ year }}/{{ month }}">{{ monthNames[month] }}</a>
</h3> </h3>
<ul class="archive"> <ul class="archive">
{% for post in postsByMonth[month.number] %} {% for post in postsByMonth[month] %}
<li> <li>
{% if post.isLink %} {% if post.isLink %}
<a href="{{ post.path }}">&rarr; {{ post.title }}</a> <a href="{{ post.path }}">&rarr; {{ post.title }}</a>
{% else %} {% else %}
<a href="{{ post.path }}">{{ post.title }}</a> <a href="{{ post.path }}">{{ post.title }}</a>
{% endif %} {% endif %}
<time>{{ post.date.day }} {{ month.abbreviatedName }}</time> <time>{{ post.day }} {{ monthAbbreviations[month] }}</time>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}