diff --git a/Readme.md b/Readme.md index 7af7519..66f4eda 100644 --- a/Readme.md +++ b/Readme.md @@ -15,7 +15,7 @@ This version will go back to its roots and use headers at the top of markdown fi Execution, trying TDD for the first time: -- [ ] Replace harp with custom Swift code +- [x] Replace harp with custom Swift code - [x] Write a test harness that renders a site and then checks the output with `diff -r` @@ -51,7 +51,7 @@ Execution, trying TDD for the first time: - [x] Check and delete _data.json - - [ ] Migrate posts to markdown with headers somehow + - [x] Migrate posts to markdown with headers somehow - [x] Define the new format @@ -63,15 +63,19 @@ Execution, trying TDD for the first time: - [x] Migrate month indexes - - [ ] Migrate index / recent posts + - [x] Migrate index / recent posts - - [ ] Migrate archive and put it at /posts/index.html, duh! + - [x] Migrate archive and put it at /posts/index.html, duh! - - [ ] 301 redirect /archive to /posts, and update the header link + - [x] 301 redirect /archive to /posts, and update the header link - - [ ] Check and delete _data.json filse + - [x] Check and delete _data.json filse - - [ ] Search for other _data.json files and eliminate any that are found + - [x] Search for other _data.json and .ejs files and eliminate any that are found + +- [ ] Find a way to add the site name to HTML titles rendered by plugins + +- [ ] Clean up the posts plugin - [ ] Consider using Swift for samhuri.net as well, and then making SiteGenerator a package that it uses ... then we can use Plot or pointfree.co's swift-html diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift b/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift index b3d5338..05b3924 100644 --- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift +++ b/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift @@ -50,19 +50,12 @@ final class PostsPlugin: Plugin { return } - let recentPostsURL = targetURL.appendingPathComponent(recentPostsPath) - try renderRecentPosts(targetFileURL: recentPostsURL, templateRenderer: templateRenderer) - let postsDir = targetURL.appendingPathComponent(postsPath) - try renderYearsAndMonths(postsDir: postsDir, templateRenderer: templateRenderer) try renderPostsByDate(postsDir: postsDir, templateRenderer: templateRenderer) - } - - func renderRecentPosts(targetFileURL: URL, templateRenderer: TemplateRenderer) throws { - print("renderRecentPosts(targetFileURL: \(targetFileURL), templateRenderer: \(templateRenderer)") - let recentPosts = posts.flattened().prefix(10) - let recentPostsHTML = try templateRenderer.renderTemplate(name: "recent-posts", context: ["recentPosts": recentPosts]) - try recentPostsHTML.write(to: targetFileURL, atomically: true, encoding: .utf8) + try renderYears(postsDir: postsDir, templateRenderer: templateRenderer) + try renderMonths(postsDir: postsDir, templateRenderer: templateRenderer) + try renderArchive(postsDir: postsDir, templateRenderer: templateRenderer) + try renderRecentPosts(targetURL: targetURL, templateRenderer: templateRenderer) } func renderPostsByDate(postsDir: URL, templateRenderer: TemplateRenderer) throws { @@ -75,49 +68,78 @@ final class PostsPlugin: Plugin { } } - func renderYearsAndMonths(postsDir: URL, templateRenderer: TemplateRenderer) throws { - print("renderYearsAndMonths(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") + func renderRecentPosts(targetURL: URL, templateRenderer: TemplateRenderer) throws { + print("renderRecentPosts(targetURL: \(targetURL), templateRenderer: \(templateRenderer)") + let recentPosts = posts.flattened().prefix(10) + let renderedRecentPosts: [[String: Any]] = recentPosts.map { post in + let html = markdownParser.html(from: post.bodyMarkdown) + return RenderedPost(post: post, body: html).dictionary + } + #warning("FIXME: get the site name out of here somehow") + let recentPostsHTML = try templateRenderer.renderTemplate(name: "recent-posts", context: [ + "title": "samhuri.net", + "recentPosts": renderedRecentPosts, + ]) + let fileURL = targetURL.appendingPathComponent(recentPostsPath) + try recentPostsHTML.write(to: fileURL, atomically: true, encoding: .utf8) + } + + func renderArchive(postsDir: URL, templateRenderer: TemplateRenderer) throws { + print("renderArchive(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") + let allYears = posts.byYear.keys.sorted(by: >) + let allMonths = (1 ... 12).map(Month.init(_:)) + let postsByMonthByYearForContext: [[String: Any]] = allYears.map { year in + [ + "number": "\(year)", + "months": posts[year].byMonth.keys.sorted(by: >).map { month in + [ + "number": month.padded, + "posts": posts[year][month].posts, + ] + } as Any, + ] + } + let context: [String: Any] = [ + "title": "Archive", + "path": postsPath, + "years": postsByMonthByYearForContext, + "monthNames": allMonths.reduce(into: [String: String](), { dict, month in + dict[month.padded] = month.name + }), + "monthAbbreviations": allMonths.reduce(into: [String: String](), { dict, month in + dict[month.padded] = month.abbreviatedName + }), + ] + let archiveHTML = try templateRenderer.renderTemplate(name: "posts-archive", context: context) + let archiveURL = postsDir.appendingPathComponent("index.html") + try archiveHTML.write(to: archiveURL, atomically: true, encoding: .utf8) + } + + func renderYears(postsDir: URL, templateRenderer: TemplateRenderer) throws { + print("renderYears(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") let allMonths = (1 ... 12).map(Month.init(_:)) for (year, monthPosts) in posts.byYear.sorted(by: { $1.key < $0.key }) { let yearDir = postsDir.appendingPathComponent("\(year)") - var sortedPostsByMonth: [Month: [RenderedPost]] = [:] + var sortedPostsByMonth: [Month: [Post]] = [:] for month in allMonths { let sortedPosts = monthPosts[month].posts.sorted(by: { $1.date < $0.date }) - guard !sortedPosts.isEmpty else { - continue + if !sortedPosts.isEmpty { + sortedPostsByMonth[month] = sortedPosts } - - let renderedPosts = sortedPosts.map { post -> RenderedPost in - let bodyHTML = markdownParser.html(from: post.bodyMarkdown) - return RenderedPost(post: post, body: bodyHTML) - } - sortedPostsByMonth[month] = renderedPosts - - let monthDir = yearDir.appendingPathComponent(month.padded) - try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) - #warning("FIXME: get the site name out of here somehow") - let context: [String: Any] = [ - "title": "samhuri.net: \(month.name) \(year)", - "posts": renderedPosts.map { $0.dictionary }, - ] - let monthHTML = try templateRenderer.renderTemplate(name: "posts-month", context: context) - let monthURL = monthDir.appendingPathComponent("index.html") - try monthHTML.write(to: monthURL, atomically: true, encoding: .utf8) } 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 postsByMonthForContext: [String: [Post]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in + let (month, posts) = pair + dict[month.padded] = posts } - let monthsPadded = months.map { $0.padded } - #warning("FIXME: get the site name out of here somehow") + #warning("FIXME: get the site name in the head title but not the body title") let context: [String: Any] = [ - "title": "samhuri.net: \(year)", + "title": "\(year)", "path": postsPath, "year": year, - "months": monthsPadded, + "months": months.map { $0.padded }, "monthNames": months.reduce(into: [String: String](), { dict, month in dict[month.padded] = month.name }), @@ -132,6 +154,35 @@ final class PostsPlugin: Plugin { } } + func renderMonths(postsDir: URL, templateRenderer: TemplateRenderer) throws { + print("renderMonths(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") + let allMonths = (1 ... 12).map(Month.init(_:)) + for (year, monthPosts) in posts.byYear.sorted(by: { $1.key < $0.key }) { + let yearDir = postsDir.appendingPathComponent("\(year)") + for month in allMonths { + let sortedPosts = monthPosts[month].posts.sorted(by: { $1.date < $0.date }) + guard !sortedPosts.isEmpty else { + continue + } + + let renderedPosts = sortedPosts.map { post -> RenderedPost in + let bodyHTML = markdownParser.html(from: post.bodyMarkdown) + return RenderedPost(post: post, body: bodyHTML) + } + let monthDir = yearDir.appendingPathComponent(month.padded) + try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) + #warning("FIXME: get the site name in the head title but not the body title") + let context: [String: Any] = [ + "title": "\(month.name) \(year)", + "posts": renderedPosts.map { $0.dictionary }, + ] + let monthHTML = try templateRenderer.renderTemplate(name: "posts-month", context: context) + let monthURL = monthDir.appendingPathComponent("index.html") + try monthHTML.write(to: monthURL, atomically: true, encoding: .utf8) + } + } + } + private func renderPost(_ post: Post, monthDir: URL, templateRenderer: TemplateRenderer) throws { print("renderPost(\(post.debugDescription), monthDir: \(monthDir), templateRenderer: \(templateRenderer)") try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) @@ -139,9 +190,9 @@ final class PostsPlugin: Plugin { let postURL = monthDir.appendingPathComponent(filename) let bodyHTML = markdownParser.html(from: post.bodyMarkdown) let renderedPost = RenderedPost(post: post, body: bodyHTML) - #warning("FIXME: get the site name out of here somehow") + #warning("FIXME: get the site name in the head title but not the body title") let postHTML = try templateRenderer.renderTemplate(name: "post", context: [ - "title": "samhuri.net: \(renderedPost.post.title)", + "title": "\(renderedPost.post.title)", "post": renderedPost.dictionary, ]) try postHTML.write(to: postURL, atomically: true, encoding: .utf8) diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift index 4262ecc..843e89d 100644 --- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift +++ b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift @@ -46,9 +46,9 @@ final class ProjectsPlugin: Plugin { let projectsDir = targetURL.appendingPathComponent(path) try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil) let projectsURL = projectsDir.appendingPathComponent("index.html") - #warning("FIXME: get the site name out of here somehow") + #warning("FIXME: get the site name in the head title but not the body title") let projectsHTML = try templateRenderer.renderTemplate(name: "projects", context: [ - "title": "samhuri.net: Projects", + "title": "Projects", "projects": projects, ]) try projectsHTML.write(to: projectsURL, atomically: true, encoding: .utf8) @@ -56,9 +56,9 @@ final class ProjectsPlugin: Plugin { for project in projects { let filename = "\(project.title).html" let projectURL = projectsDir.appendingPathComponent(filename) - #warning("FIXME: get the site name out of here somehow") + #warning("FIXME: get the site name in the head title but not the body title") let projectHTML = try templateRenderer.renderTemplate(name: "project", context: [ - "title": "samhuri.net: \(project.title)", + "title": "\(project.title)", "project": project, ]) try projectHTML.write(to: projectURL, atomically: true, encoding: .utf8) diff --git a/drafts/_data.json b/drafts/_data.json deleted file mode 100644 index a11067c..0000000 --- a/drafts/_data.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "mach-o-symbol-and-relocation-tables": { - "id": "mach-o-symbol-and-relocation-tables", - "author": "Sami Samhuri", - "title": "Mach-O Symbol and Relocation Tables", - "date": "28th June, 2015", - "timestamp": 1435527198, - "link": null, - "url": "/posts/drafts/mach-o-symbol-and-relocation-tables", - "tags": [ - - ] - }, - "09c2b186-3ce7-445c-89bf-5d5b7f830cd7": { - "id": "09c2b186-3ce7-445c-89bf-5d5b7f830cd7", - "author": "Sami Samhuri", - "title": "The Case for Native", - "date": "27th June, 2015", - "timestamp": 1435424525, - "link": null, - "url": "/posts/drafts/09c2b186-3ce7-445c-89bf-5d5b7f830cd7", - "tags": [ - - ] - }, - "security-through-obscurity-is-still-not-a-best-practice": { - "id": "security-through-obscurity-is-still-not-a-best-practice", - "author": "Sami Samhuri", - "title": "security-through-obscurity-is-still-not-a-best-practice", - "date": "20th August, 2017", - "timestamp": 1503246688, - "link": null, - "url": "/posts/2017/08/security-through-obscurity-is-still-not-a-best-practice", - "tags": [ - "rails", - "security" - ] - } -} \ No newline at end of file diff --git a/drafts/mach-o-symbol-and-relocation-tables.md b/drafts/mach-o-symbol-and-relocation-tables.md index d4daa2e..b90591f 100644 --- a/drafts/mach-o-symbol-and-relocation-tables.md +++ b/drafts/mach-o-symbol-and-relocation-tables.md @@ -1,3 +1,11 @@ +--- +Title: Mach-O Symbol and Relocation Tables +Author: Sami Samhuri +Date: 28th June, 2015 +Timestamp: 1435527198 +Tags: +--- + The latest technology I've been learning is Palm's SDK for webOS, Mojo. My first impression is that it's a great platform and Palm could do a great job of 2.0 if they cut down on some of the @@ -32,4 +40,4 @@ support for Emacs](http://www.emacswiki.org/emacs/MojoSdk) which provided a great base to get started with. There are wrappers around (all?) of the Palm SDK commands but it needed a bit of work to make it just do what I -wanted with as little input and thought as possible. \ No newline at end of file +wanted with as little input and thought as possible. diff --git a/drafts/security-through-obscurity-is-still-not-a-best-practice.md b/drafts/security-through-obscurity-is-still-not-a-best-practice.md index 66178e1..c38f862 100644 --- a/drafts/security-through-obscurity-is-still-not-a-best-practice.md +++ b/drafts/security-through-obscurity-is-still-not-a-best-practice.md @@ -1,3 +1,11 @@ +--- +Title: TBD +Author: Sami Samhuri +Date: 20th August, 2017 +Timestamp: 1503246688 +Tags: rails, security +--- + A common way to configure a Rails server for different deployment environments is to use environment variables. This is a good practice as described by [The Twelve-Factor App][12factor] and can be applied to any server framework in any language running on a Unix OS. It keeps such secrets out of your code repository which is good, and it also makes it easy to customize your application for different environments. It's a pretty solid technique. [12factor]: https://12factor.net diff --git a/drafts/09c2b186-3ce7-445c-89bf-5d5b7f830cd7.md b/drafts/the-case-for-native.md similarity index 98% rename from drafts/09c2b186-3ce7-445c-89bf-5d5b7f830cd7.md rename to drafts/the-case-for-native.md index 3a29616..38fe307 100644 --- a/drafts/09c2b186-3ce7-445c-89bf-5d5b7f830cd7.md +++ b/drafts/the-case-for-native.md @@ -1,3 +1,11 @@ +--- +Title: The Case for Native +Author: Sami Samhuri +Date: 27th June, 2015 +Timestamp: 1435424525 +Tags: +--- + For the past month I've been using [Appcelerator Titanium](http://www.appcelerator.com/products/) on a two man team. We made a simple iPhone app with a tab bar with embedded nav controllers, just 10 screens or so total. We started porting it to the iPad and Android so have some experience there. It's been a pretty frustrating exercise most days. I had a lot of little complaints but didn't take the time to step back and look at the bigger picture. I love JavaScript and in theory Titanium is awesome and a huge win. I wanted it to be a win but in reality it just hasn't been. Here are 9 reasons why native is better in the short and long run. diff --git a/public/.htaccess b/public/.htaccess index 59717bf..53090a5 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -79,8 +79,10 @@ Redirect 301 /json-diff http://tlrobinson.net/projects/javascript-fun/jsondiff # The great vowel shortage of 1974 is over. Redirect 301 /proj /projects +Redirect 301 /archive /posts +Redirect 301 /archive/index.html /posts Redirect 301 /blog/sjs.rss /feed.xml -Redirect 301 /blog /posts +Redirect 301 /blog / # Old naming scheme, incompatible with Harp. Redirect 301 /blog/2006.02.08-first-post /posts/2006/02/first-post diff --git a/public/archive.ejs b/public/archive.ejs deleted file mode 100644 index dd30743..0000000 --- a/public/archive.ejs +++ /dev/null @@ -1,53 +0,0 @@ -<% function pad(n) { -%> -<% return +n < 10 ? '0' + n : String(n) -%> -<% } -%> - -<% var _months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ') -%> -<% function formatDate(t) { -%> -<% var d = new Date(t) -%> -<% return d.getDate() + ' ' + _months[d.getMonth()] -%> -<% } -%> - -<% function sortPosts(dir) { -%> -<% var slugs = Object.keys(dir._data) -%> -<% var posts = slugs.map(function(slug) { return dir._data[slug] }) -%> -<% posts.sort(function(a, b) { -%> -<% var t1 = a.timestamp -%> -<% var t2 = b.timestamp -%> -<% return t1 < t2 ? 1 : (t1 > t2 ? -1 : 0) -%> -<% }) -%> -<% return posts -%> -<% } -%> - -<% function posts(dir, year) { -%> -<% if (!dir) return -%> - -