Now with docs.

master
Manager Bot 3 years ago
parent c154d006e6
commit 5999701be1
  1. 286
      libs/MJB-Backend-Jekyll/lib/MJB/Backend/Jekyll.pm

@ -9,7 +9,9 @@ use Mojo::File;
use MJB::Backend::Jekyll::MarkdownFile; use MJB::Backend::Jekyll::MarkdownFile;
use MJB::Backend::Jekyll::ConfigFile; use MJB::Backend::Jekyll::ConfigFile;
# The root path for the repositories #==
# The path to the directory that will be used to hold the git repositories for each blog blogs.
#==
has root => ( has root => (
is => 'ro', is => 'ro',
required => 1, required => 1,
@ -19,30 +21,25 @@ has root => (
}, },
); );
# The domain name for this jekyll blog #==
# The domain name for this specific blog.
#==
has domain => ( has domain => (
is => 'ro', is => 'ro',
required => 1, required => 1,
); );
# The full path to the git repo this is backed by. #==
# The full remote path ( i.e. git@foo.com:mjb/domain.com.git) to the git repository that we will push to.
#==
has repo => ( has repo => (
is => 'ro', is => 'ro',
required => 1, required => 1,
); );
has config => ( #==
is => 'lazy', # The full local path (i.e. /var/repos/domain.com ) to the git repository that we will execute git commands in.
); #==
sub _build_config {
my ( $self ) = @_;
return MJB::Backend::Jekyll::ConfigFile->new(
path => $self->repo_path . "/_config.yml",
)->read;
}
has repo_path => ( has repo_path => (
is => 'lazy', is => 'lazy',
); );
@ -53,13 +50,39 @@ sub _build_repo_path {
return $self->root . "/" . $self->domain; return $self->root . "/" . $self->domain;
} }
# The full path to the git repo to clone when using #==
# init on a new repository. # The full git path (i.e. git@foo.com:mjb/default-site.git) of the repository to use as an initial
# template when using the init() method to create a new blog.
#==
has init_from => ( has init_from => (
is => 'ro', is => 'ro',
required => 1, required => 1,
); );
#==
# The configuration file for the Jekyll blog itself.
#==
has config => (
is => 'lazy',
);
sub _build_config {
my ( $self ) = @_;
return MJB::Backend::Jekyll::ConfigFile->new(
path => $self->repo_path . "/_config.yml",
)->read;
}
#==
# This method will initialize a new blog.
#
# It will clone the git repo from $self->init_from and set a new remote, then
# push the repository (and expect the git server to create the repo on push).
#
# It returns $self
#==
sub init { sub init {
my ( $self ) = @_; my ( $self ) = @_;
@ -92,6 +115,16 @@ sub init {
return $self; return $self;
} }
#==
# This method will list the media files that are in the blog's /assets/media directory,
# where images, pdfs, etc may be stored.
#
# It returns a list of hashrefs containing
# path | The full path to the file
# filename | The basename of the file.
# url | The http url that the image should exist at
# markdown | The markdown code to embedd the asset as an image
#==
sub list_media { sub list_media {
my ( $self ) = @_; my ( $self ) = @_;
@ -114,6 +147,15 @@ sub list_media {
return [ @return ]; return [ @return ];
} }
#==
# This method will list posts for the blog that are in the /_posts
# collection..
#
# It returns a list of MJB::Backend::Jekyll::MarkdownFile objects.
#
# For speed read() is NOT called on these objects, so the file will
# not be loaded until you call read() on the object.
#==
sub list_posts { sub list_posts {
my ( $self ) = @_; my ( $self ) = @_;
@ -134,6 +176,13 @@ sub list_posts {
return [ @files ]; return [ @files ];
} }
#==
# This method removes a markdown file from the git repository for this blog.
#
# It accepts a Mojo::File object to remove.
#
# It returns self.
#==
sub remove_markdown_file { sub remove_markdown_file {
my ( $self, $file ) = @_; my ( $self, $file ) = @_;
@ -155,6 +204,16 @@ sub remove_markdown_file {
return $self; return $self;
} }
#==
# This method lists all of the pages that exist in this blog.
#
# It will search for .md and .markdown files and returns an arrayref
# of MJB::Backend::Jekyll::MarkdownFile objects.
#
# For speed read() is NOT called on these objects, so the file will
# not be loaded until you call read() on the object.
#==
sub list_pages { sub list_pages {
my ( $self ) = @_; my ( $self ) = @_;
@ -175,6 +234,13 @@ sub list_pages {
return [ @files ]; return [ @files ];
} }
#==
# This method will load a post by its filename.
#
# It returns an MJB::Backend::Jekyll::MarkdownFile object
# if the file exists. Otherwise, it returns undef.
#
#==
sub get_post { sub get_post {
my ( $self, $filename ) = @_; my ( $self, $filename ) = @_;
@ -190,7 +256,26 @@ sub get_post {
)->read; )->read;
} }
#==
# This method will create a new post on the blog.
#
# It expects the filename for the collection in YYYY-MM-DD-some-title.markdown format.
# (i.e. 2020-12-25-it-is-christmas.markdown)
#
# It returns an MJB::Backend::Jekyll::MarkdownFile object that is expected to be
# populated by the caller.
#
# Once populated, this object should be given to write_post() to commit it.
#
# TODO: get_post and new_post are the very nearly the same, and should be refactored into
# one function.
# 1. Write a new function load_or_create_post() that will function as get_post, but
# if the post doesn't exist, it will function as new_post and return an object
# anyway.
# 2. Run ack in the controllers and update any use of get_post or new_post to use the
# new function. Confirm it works at each step of the way.
# 3. Remove get_post and new_post from this file.
#==
sub new_post { sub new_post {
my ( $self, $filename ) = @_; my ( $self, $filename ) = @_;
@ -203,6 +288,20 @@ sub new_post {
); );
} }
#==
# This method will create a new page.
#
# It expects a filepath in the form of my/file/path/and/name.markdown, and will
# return a MJB::Backend::Jekyll::MarkdownFile object.
#
# That object should be given to write_post.
#
# TODO: The difference between this and the post is that this can go off the root
# of the repo, where the posts go off the /_post/.
#
# Think about how this would work with being refactored the same as the new_post bit...
# it could be all three should be one function.
#==
sub new_page { sub new_page {
my ( $self, $filename ) = @_; my ( $self, $filename ) = @_;
@ -215,37 +314,14 @@ sub new_page {
); );
} }
sub get_title_of_post {
my ( $self, $file ) = @_;
open my $lf, "<", $file
or die "Failed to open $file for reading: $!";
while ( defined( my $line = <$lf> ) ) {
if ( $line =~ /^title: (.+)$/ ) {
close $lf;
return $1;
}
}
close $lf;
return undef;
}
# Think about this....
# probably want 'slug: ' as an override for the file path
sub _post_path {
my ( $self, $headers ) = @_;
my $title = $headers->{title};
$title = lc($title);
$title =~ s/[^a-zA-Z0-9-_]+/_/g;
$title =~ s/[_]+/_/g;
$title =~ s/_$//g;
$title =~ s/^_//g;
return $self->repo_path . "/_posts/" . $title . ".markdown";
}
#==
# This method writes the jekyll blog configuration file.
# You can pass an MJB::Backend::Jekyll::ConfigFile object to write,
# otherwise the one in $self is written.
#
# The config file is written, commited, and pushed to the git origin server.
#==
sub write_config { sub write_config {
my ( $self, $config ) = @_; my ( $self, $config ) = @_;
@ -271,6 +347,12 @@ sub write_config {
return 1; return 1;
} }
#==
# This method accepts an MJB::Backend::Jekyll::MarkdownFile object
# and writes it to the blog, then commits and pushes it to the origin.
#
# It is used by the post and page editing/creating functions.
#==
sub write_post { sub write_post {
my ( $self, $md_file ) = @_; my ( $self, $md_file ) = @_;
@ -298,6 +380,12 @@ sub write_post {
return 1; return 1;
} }
#==
# This method accepts a file path and a comment and will commit the file,
# and push the repo.
#
# This is used to commit media and non-post files.
#==
sub commit_file { sub commit_file {
my ( $self, $file, $comment ) = @_; my ( $self, $file, $comment ) = @_;
@ -323,6 +411,12 @@ sub commit_file {
} }
#==
# This method accepts a file path and a comment and will remove the file,
# and push the repo.
#
# This is used to delete media and other files.
#==
sub remove_file { sub remove_file {
my ( $self, $file, $comment ) = @_; my ( $self, $file, $comment ) = @_;
@ -348,26 +442,9 @@ sub remove_file {
} }
sub revert_commit { #==
my ( $self, $commit ) = @_; # Given a commit, restore the repo to that state.
#==
# Check that the repo exists and is latest.
$self->_ensure_repository_is_latest;
# Add the file to git
$self->system_command( [ qw( git revert --no-edit ), $commit ], {
chdir => $self->repo_path,
});
# Push the repo to the store server
$self->system_command( [ qw( git push origin master ) ], {
chdir => $self->repo_path,
});
return 1;
}
sub restore_commit { sub restore_commit {
my ( $self, $commit ) = @_; my ( $self, $commit ) = @_;
@ -393,33 +470,14 @@ sub restore_commit {
} }
sub delete_post { #==
my ( $self, $title, $file ) = @_; # This method lists the history of the git repo.
#
# Check if the repo exists and update the repo if needed # It returns an arrayref of hashrefs with the following keys:
$self->_ensure_repository_is_latest; # commit | The commit hash
# dateref | The date, expressed relative (i.e. 3 days ago)
# Ensure the post exists - irony # message | The commit message
die "Error: Cannot delete post that doesn't exists at " . $file #==
if ! -f $file;
# git rm the file
$self->system_command( [ qw( git rm ), $file ], {
chdir => $self->repo_path,
});
# git commit the file
$self->system_command( [ qw( git commit -m ), "Deleted post $title" ], {
chdir => $self->repo_path,
});
# Push the repo to the store server
$self->system_command( [ qw( git push origin master ) ], {
chdir => $self->repo_path,
});
}
sub history { sub history {
my ( $self ) = @_; my ( $self ) = @_;
@ -449,8 +507,11 @@ sub history {
return [ @return ]; return [ @return ];
} }
# Helper function to ensure the repo exists and has the latest #==
# changes. # This method will checkout the repo if it doesn't exist on the filesystem.
#
# It will update the repo with a git pull.
#==
sub _ensure_repository_is_latest { sub _ensure_repository_is_latest {
my ( $self ) = @_; my ( $self ) = @_;
@ -468,6 +529,37 @@ sub _ensure_repository_is_latest {
return 1; return 1;
} }
#==
# Run a system command.
# $self->system_command(
# [qw( the command here )],
# { options => 'here' },
# );
#
# This method accepts an arrayref with a command, and a hashref with
# options.
#
# The command will be executed.
#
# The following options may be passed:
# chdir | Directory to chdir to before executing the command
# mask | A hash like { My$ecretP@ssword => '--PASSWORD--' } to censor in
# logging STDOUT/STDERR, and logging the command itself.
# fail_on_stderr | An arrayref like [
# qr/pattern/ => 'die reason',
# qr/other pattern/ => 'another die reason'
# ] where system_command will emmit a die if the pattern matches on stderr.
#
# A hashref will be returned that contains the following keys:
# {
# stdout => 'standard output content',
# stderr => 'standard error content',
# exitno => 1, # the exit status of the command
# }
#
# If the environment variable MJB_DEBUG is set true, these return values
# will also be printed to STDOUT.
#==
sub system_command { sub system_command {
my ( $self, $cmd, $settings ) = @_; my ( $self, $cmd, $settings ) = @_;

Loading…
Cancel
Save