150 lines
4.8 KiB
Raku
150 lines
4.8 KiB
Raku
use v6.e.PREVIEW;
|
|
|
|
#| Post database
|
|
unit module DB;
|
|
|
|
use Pandoc;
|
|
use JSON::Class:auth<zef:vrurg>;
|
|
use XML;
|
|
use XQ;
|
|
|
|
use DB::Post;
|
|
use DB::BlogMeta;
|
|
use DB::MarkdownPost;
|
|
use DB::IdrisPost;
|
|
use DB::PlaceholderPost;
|
|
use Atom;
|
|
use Config;
|
|
|
|
subset PostTypes where MarkdownPost:D | IdrisPost:D | PlaceholderPost:D;
|
|
|
|
#| The top level posts database
|
|
class PostDB {
|
|
#| The metadata for the blog
|
|
has BlogMeta:D $.meta is required;
|
|
#| A mapping from post ids to posts
|
|
# has %.posts is Posts;
|
|
has %.posts{Int} of PostTypes = %();
|
|
#| The post id to use for placeholder posts
|
|
has Int $.placeholder-id = 0;
|
|
|
|
#| Get the next unused post ID
|
|
method next-post-id(--> Int) {
|
|
if %!posts.elems > 0 {
|
|
%!posts.keys.max + 1
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
#| Insert a new post to the DB, returning its id
|
|
method insert-post(PostTypes $post --> Int) {
|
|
my $id = self.next-post-id;
|
|
%!posts{$id} = $post;
|
|
$id
|
|
}
|
|
|
|
#| Initialize a new database
|
|
method init(BlogMeta:D $meta --> PostDB:D) {
|
|
my %posts{Int} of PostTypes = %();
|
|
%posts{0} = PlaceholderPost.empty;
|
|
PostDB.new(
|
|
meta => $meta,
|
|
posts => %posts,
|
|
)
|
|
}
|
|
|
|
#| Write a database to a directory
|
|
method write(IO::Path:D $dir) {
|
|
my $posts-dir = $dir.add('posts/');
|
|
# Make sure directory structrue exists
|
|
mkdir $dir unless $dir.e;
|
|
mkdir $posts-dir unless $posts-dir.e;
|
|
# Write out metadata
|
|
# TODO: Track changes and only write changed files
|
|
$dir.add('meta.json').spurt: $!meta.to-json;
|
|
# Write out posts (ids are the filename)
|
|
for %!posts.kv -> $key, $value {
|
|
$posts-dir.add("$key.json").spurt: $value.to-json;
|
|
}
|
|
}
|
|
|
|
#| Render the site to the provided output directory
|
|
method render(IO::Path:D $out-dir, Config:D :$config = Config.new) {
|
|
my $posts = $out-dir.add('posts/');
|
|
my $by-id = $posts.add('by-id/');
|
|
my $by-slug = $posts.add('by-slug/');
|
|
# Make sure the directory structure exists
|
|
mkdir $out-dir unless $out-dir.e;
|
|
mkdir $posts unless $posts.e;
|
|
mkdir $by-id unless $by-id.e;
|
|
mkdir $by-slug unless $by-slug.e;
|
|
# Render all the posts and make symlinks
|
|
for %!posts.kv -> $id, $post {
|
|
my $html = $config.generate-post: $post, $!meta;
|
|
my $id-path = $by-id.add: "$id.html";
|
|
$id-path.spurt: $html;
|
|
for $post.all-slugs -> $slug {
|
|
# remove the symlink if it already exists
|
|
my $slug-path = $by-slug.add: "$slug.html";
|
|
$slug-path.unlink if $slug-path.l;
|
|
$id-path.symlink: $slug-path;
|
|
}
|
|
}
|
|
# Render the index
|
|
$out-dir.add('index.html').spurt: $config.generate-index(self);
|
|
# Render the archive
|
|
$out-dir.add('archive.html').spurt: $config.generate-archive(self);
|
|
# Symlink the about article
|
|
my $about-path = $out-dir.add('about.html');
|
|
$about-path.unlink if $about-path.l;
|
|
$by-id.add("{$!meta.about-id}.html").symlink: $about-path;
|
|
# Render the rss/atom feed
|
|
my $atom-path = $out-dir.add('atom.xml');
|
|
my $atom = posts-to-atom self;
|
|
$atom-path.spurt: format-xml(~$atom);
|
|
# Create the resources folder and copy over our style sheets
|
|
my $res-dir = $out-dir.add('resources/');
|
|
mkdir $res-dir unless $res-dir.e;
|
|
$res-dir.add('colors.css').spurt: %?RESOURCES<colors.css>.slurp;
|
|
$res-dir.add('main.css').spurt: %?RESOURCES<main.css>.slurp;
|
|
$res-dir.add('code.css').spurt: %?RESOURCES<code.css>.slurp;
|
|
}
|
|
|
|
#| Get a list of posts sorted by date
|
|
method sorted-posts() {
|
|
%!posts.sort(*.value.posted-at).reverse
|
|
}
|
|
}
|
|
|
|
#| Read the database out of a directory
|
|
sub read-db(IO::Path:D $dir --> PostDB:D) is export {
|
|
my $posts-dir = $dir.add('posts/');
|
|
die "DB directory does not exist" unless $dir.e;
|
|
die "posts directory does not exist" unless $posts-dir.e;
|
|
# Read metadata
|
|
my $meta = BlogMeta.from-json: $dir.add('meta.json').slurp;
|
|
# Read posts
|
|
my %posts{Int} of PostTypes = %();
|
|
for dir $posts-dir -> $post {
|
|
my $id = $post.extension("").basename.Int;
|
|
# TODO: Dejankify this, maybe see if we can work with parsed, but
|
|
# unmarshalled json
|
|
given $post.slurp {
|
|
when /'"placeholder": true'/ {
|
|
%posts{$id} = PlaceholderPost.from-json: $_;
|
|
}
|
|
when /'"markdown": true'/ {
|
|
%posts{$id} = MarkdownPost.from-json: $_;
|
|
}
|
|
when /'"idris": true'/ {
|
|
%posts{$id} = IdrisPost.from-json: $_;
|
|
}
|
|
default {
|
|
die "Unsupported post type: $post";
|
|
}
|
|
}
|
|
}
|
|
# Build db structure
|
|
PostDB.new: meta => $meta, posts => %posts
|
|
}
|