use v6.e.PREVIEW; #| Post database unit module DB; use Pandoc; use JSON::Class:auth; use DB::Post; use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; use DB::PlaceholderPost; 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); # TODO: 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; # TODO: Render the rss/atom feed die "Not Implemented" } #| 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 }