~duncan-bayne/halp

cf49e4868c4bbe53bb2ff05e970b050338eaa58e — Duncan Bayne a month ago 40fd5ee
Refactor Web and Gemini generators into a single class
34 files changed, 42 insertions(+), 190 deletions(-)

R gemini/site/.static/images/atom.png => gmi/site/.static/images/atom.png
R gemini/site/.static/images/bvbrows.gif => gmi/site/.static/images/bvbrows.gif
R gemini/site/.static/images/favicon.png => gmi/site/.static/images/favicon.png
R gemini/site/.static/images/halp-icon.jpg => gmi/site/.static/images/halp-icon.jpg
R gemini/site/.static/images/mothracompat.gif => gmi/site/.static/images/mothracompat.gif
R gemini/site/.static/images/valid-atom.png => gmi/site/.static/images/valid-atom.png
R gemini/site/.static/images/valid-html5.png => gmi/site/.static/images/valid-html5.png
R gemini/site/.static/images/vcss-blue => gmi/site/.static/images/vcss-blue
R gemini/templates/directory_listing.gmi.tpl => gmi/templates/directory_listing.gmi.tpl
R gemini/templates/menu.gmi.tpl => gmi/templates/menu.gmi.tpl
R gemini/templates/page.gmi.tpl => gmi/templates/page.gmi.tpl
R gemini/templates/sitemap.gmi.tpl => gmi/templates/sitemap.gmi.tpl
M halp.pl
R www/site/.static/images/atom.png => html/site/.static/images/atom.png
R www/site/.static/images/avatar.jpg => html/site/.static/images/avatar.jpg
R www/site/.static/images/bvbrows.gif => html/site/.static/images/bvbrows.gif
R www/site/.static/images/favicon.png => html/site/.static/images/favicon.png
R www/site/.static/images/halp-icon.jpg => html/site/.static/images/halp-icon.jpg
R www/site/.static/images/mothracompat.gif => html/site/.static/images/mothracompat.gif
R www/site/.static/images/valid-atom.png => html/site/.static/images/valid-atom.png
R www/site/.static/images/valid-html5.png => html/site/.static/images/valid-html5.png
R www/site/.static/images/vcss-blue => html/site/.static/images/vcss-blue
R www/site/.static/styles/halp.css => html/site/.static/styles/halp.css
R www/templates/directory_listing.html.tpl => html/templates/directory_listing.html.tpl
R www/templates/menu.html.tpl => html/templates/menu.html.tpl
R www/templates/page.html.tpl => html/templates/page.html.tpl
R www/templates/sitemap.html.tpl => html/templates/sitemap.html.tpl
D lib/Halp/GeminiGenerator.pm
R lib/Halp/{WebGenerator.pm => Generator.pm}
M t/Halp/generation_test.t
A t/expected/gemini/.gitkeep
A t/expected/www/.gitkeep
A tmp/gemini/.gitkeep
A tmp/www/.gitkeep
R gemini/site/.static/images/atom.png => gmi/site/.static/images/atom.png +0 -0
R gemini/site/.static/images/bvbrows.gif => gmi/site/.static/images/bvbrows.gif +0 -0
R gemini/site/.static/images/favicon.png => gmi/site/.static/images/favicon.png +0 -0
R gemini/site/.static/images/halp-icon.jpg => gmi/site/.static/images/halp-icon.jpg +0 -0
R gemini/site/.static/images/mothracompat.gif => gmi/site/.static/images/mothracompat.gif +0 -0
R gemini/site/.static/images/valid-atom.png => gmi/site/.static/images/valid-atom.png +0 -0
R gemini/site/.static/images/valid-html5.png => gmi/site/.static/images/valid-html5.png +0 -0
R gemini/site/.static/images/vcss-blue => gmi/site/.static/images/vcss-blue +0 -0
R gemini/templates/directory_listing.gmi.tpl => gmi/templates/directory_listing.gmi.tpl +0 -0
R gemini/templates/menu.gmi.tpl => gmi/templates/menu.gmi.tpl +0 -0
R gemini/templates/page.gmi.tpl => gmi/templates/page.gmi.tpl +0 -0
R gemini/templates/sitemap.gmi.tpl => gmi/templates/sitemap.gmi.tpl +0 -0
M halp.pl => halp.pl +9 -7
@@ 5,8 5,7 @@ use strict;
use warnings;

use Getopt::Long;
use Halp::GeminiGenerator;
use Halp::WebGenerator;
use Halp::Generator;

my %halp_config;
my $config_filename = '';


@@ 31,24 30,27 @@ my $config_hash = do($config_filename);
%halp_config = %$config_hash;

if ($web) {
    my $web_server = Halp::WebGenerator->new(
    my $web_server = Halp::Generator->new(
	author => $halp_config{author},
	domain => $halp_config{domain},
	host => $halp_config{web}{host},
	include_stylesheets => 1,
	input_path => $halp_config{web}{input_path},
	output_path => $halp_config{web}{output_path}
	output_path => $halp_config{web}{output_path},
	template_type => 'html'
	);
    $web_server->generate();
}

if ($gemini) {
    my $gemini_server = Halp::GeminiGenerator->new(
    my $gemini_server = Halp::Generator->new(
	author => $halp_config{author},
	domain => $halp_config{domain},
	host => $halp_config{gemini}{host},
	include_stylesheets => 0,
	input_path => $halp_config{gemini}{input_path},
	output_path => $halp_config{gemini}{output_path}
	output_path => $halp_config{gemini}{output_path},
	template_type => 'gmi'
	);

    $gemini_server->generate();
}

R www/site/.static/images/atom.png => html/site/.static/images/atom.png +0 -0
R www/site/.static/images/avatar.jpg => html/site/.static/images/avatar.jpg +0 -0
R www/site/.static/images/bvbrows.gif => html/site/.static/images/bvbrows.gif +0 -0
R www/site/.static/images/favicon.png => html/site/.static/images/favicon.png +0 -0
R www/site/.static/images/halp-icon.jpg => html/site/.static/images/halp-icon.jpg +0 -0
R www/site/.static/images/mothracompat.gif => html/site/.static/images/mothracompat.gif +0 -0
R www/site/.static/images/valid-atom.png => html/site/.static/images/valid-atom.png +0 -0
R www/site/.static/images/valid-html5.png => html/site/.static/images/valid-html5.png +0 -0
R www/site/.static/images/vcss-blue => html/site/.static/images/vcss-blue +0 -0
R www/site/.static/styles/halp.css => html/site/.static/styles/halp.css +0 -0
R www/templates/directory_listing.html.tpl => html/templates/directory_listing.html.tpl +0 -0
R www/templates/menu.html.tpl => html/templates/menu.html.tpl +0 -0
R www/templates/page.html.tpl => html/templates/page.html.tpl +0 -0
R www/templates/sitemap.html.tpl => html/templates/sitemap.html.tpl +0 -0
D lib/Halp/GeminiGenerator.pm => lib/Halp/GeminiGenerator.pm +0 -163
@@ 1,163 0,0 @@
package Halp::GeminiGenerator;

use strict;
use warnings;

use Cwd;
use Data::Dump qw(dump);
use File::Basename;
use File::Copy::Recursive "dircopy";
use File::Slurp;
use File::Spec qw(abs2rel rel2abs);
use Halp::AtomFeed;
use Halp::ContentUtils;

sub new {
    my ($class, %args) = @_;
    my $self = {};

    if (!File::Spec->file_name_is_absolute($args{input_path})) {
	$args{input_path} = File::Spec->rel2abs($args{input_path}, cwd());
    }

    if (!File::Spec->file_name_is_absolute($args{output_path})) {
	$args{output_path} = File::Spec->rel2abs($args{output_path}, cwd());
    }

    $self->{author} = $args{author};
    $self->{domain} = $args{domain};
    $self->{host} = $args{host};
    $self->{input_path} = $args{input_path};
    $self->{output_path} = $args{output_path};

    $self->{template_path} = File::Spec->catfile(cwd(), "gemini/templates");
    $self->{default_site_path} = File::Spec->catfile(cwd(), "gemini/site");

    return bless $self, $class;
}

sub generate_menu {
    my ($self, $request_path) = @_;

    my $template_pathname = File::Spec->catfile($self->{template_path}, 'menu.gmi.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);

    my @items = ({ href => '/', title => 'Home' });
    my @listing = directory_listing($self->{input_path}, $self->{input_path}, 0);
    push(@items, @listing);

    my $result = $template->fill_in(HASH => { 'items' => \@items } );
    return $result;
}

sub wrap_page_template {
    my ($self, $title, $content, $show_atom_link, $source_filename) = @_;

    my $menu = $self->generate_menu($self->{input_path});
    my $template_pathname = File::Spec->catfile($self->{template_path}, 'page.gmi.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my $atom_url = File::Spec->catfile(dirname(File::Spec->abs2rel($source_filename, $self->{input_path})), "feed.xml");

    my $footer = '';
    my $footer_pathname = '';
    if (-f $source_filename) {
	$footer_pathname = File::Spec->catfile(dirname($source_filename), '.footer.gmi');
    } elsif (-d $source_filename) {
	$footer_pathname = File::Spec->catfile($source_filename, '.footer.gmi');
    }
    if (-f $footer_pathname) {
	$footer = read_file($footer_pathname);
    }

    my $atom_feed = '';
    if ($show_atom_link) {
	$atom_feed = "=> $atom_url Atom Feed\n"
    }

    my $result = $template->fill_in(HASH => {'title' => $title, 'content' => $content, 'menu' => $menu, 'atom_feed' => $atom_feed, 'footer' => $footer});

    return $result;
}

sub generate_file {
    my ($self, $source_file) = @_;

    my $relative_file = File::Spec->abs2rel($source_file, $self->{input_path});
    my $output_file = File::Spec->catfile($self->{output_path}, $relative_file);

    my ($extension) = $source_file =~ /\.([^.]*)$/;
    my $content = read_file($source_file, binmode => ':bytes');

    if ($extension eq "gmi") {
	$content = $self->wrap_page_template(file_title($source_file), $content, 0, $source_file);
    }

    open my $content_file, '>', $output_file or die "Can't open '$output_file'";
    binmode $content_file;
    print $content_file $content;
    close $content_file;
}

sub generate_directory {
    my ($self, $source_path) = @_;

    my $relative_path = File::Spec->abs2rel($source_path, $self->{input_path});
    my $output_path = File::Spec->catfile($self->{output_path}, $relative_path);

    if (!-e $output_path) {
	mkdir($output_path) or die "Unable to create directory '$output_path' - $!";
    }

    my $title = directory_title($source_path);

    my $directory_description = "";
    my $description_pathname = File::Spec->catfile($source_path, '.description.gmi');
    if (-f $description_pathname) {
	$directory_description = read_file($description_pathname);
	$directory_description =~ s/\s+$//;
    }

    if ($relative_path !~ /^\..+$/) {
	my $template_pathname = File::Spec->catfile($self->{template_path}, 'directory_listing.gmi.tpl');
	my $template = Text::Template->new(SOURCE => $template_pathname);
	my @items = directory_listing($self->{input_path}, $source_path, 0);
	my $result = $template->fill_in(HASH => {'path' => $source_path,
						     'directory_description' => $directory_description,
						     'items' => \@items});
	$result = $self->wrap_page_template($title, $result, 1, $source_path);

	my $index_file = File::Spec->catfile($output_path, "index.gmi");
	open my $file, '>', $index_file or die "Can't open '$index_file'";
	print $file $result;
	close $file;

	my $atom_file = File::Spec->catfile($output_path, "feed.xml");
	my $atom_feed = feed_for($self->{host}, $source_path, $self->{input_path}, $self->{domain}, $self->{author});
	open $file, '>', $atom_file or die "Can't open '$index_file'";
	print $file $atom_feed;
	close $file;
    }

    my @files = <"$source_path/*" "$source_path/.*">;
    foreach my $filename (@files) {
	my $relative_file = basename($filename);
	if ($relative_file =~ m/^\.+$/) {
	    # Ignore . and ..
	} elsif (-f $filename && $relative_file =~ m/^\./) {
	    # Ignore dotfiles
	} elsif (-d $filename) {
	    $self->generate_directory($filename);
	} elsif (-f $filename) {
	    $self->generate_file($filename);
	}
    }
}

sub generate {
    my ($self) = @_;

    dircopy($self->{default_site_path}, $self->{output_path}) or die $!;
    $self->generate_directory($self->{input_path});
}

1;

R lib/Halp/WebGenerator.pm => lib/Halp/Generator.pm +31 -20
@@ 1,7 1,8 @@
package Halp::WebGenerator;
package Halp::Generator;

use strict;
use warnings;
use feature "switch";

use Cwd;
use Data::Dump qw(dump);


@@ 25,17 26,16 @@ sub new {
	$args{output_path} = File::Spec->rel2abs($args{output_path}, cwd());
    }

    # die "input_path $args{input_path} must be absolute" unless
    # die "output_path $args{output_path} must be absolute" unless File::Spec->file_name_is_absolute($args{output_path});

    $self->{author} = $args{author};
    $self->{domain} = $args{domain};
    $self->{host} = $args{host};
    $self->{include_stylesheets} = $args{include_stylesheets};
    $self->{input_path} = $args{input_path};
    $self->{output_path} = $args{output_path};
    $self->{template_type} = $args{template_type};

    $self->{template_path} = File::Spec->catfile(cwd(), "www/templates");
    $self->{default_site_path} = File::Spec->catfile(cwd(), "www/site");
    $self->{template_path} = File::Spec->catfile(cwd(), "$self->{template_type}/templates");
    $self->{default_site_path} = File::Spec->catfile(cwd(), "$self->{template_type}/site");

    return bless $self, $class;
}


@@ 43,7 43,7 @@ sub new {
sub generate_menu {
    my ($self, $request_path) = @_;

    my $template_pathname = File::Spec->catfile($self->{template_path}, 'menu.html.tpl');
    my $template_pathname = File::Spec->catfile($self->{template_path}, "menu.$self->{template_type}.tpl");
    my $template = Text::Template->new(SOURCE => $template_pathname);

    my @items = ({ href => '/', title => 'Home' });


@@ 58,16 58,16 @@ sub wrap_page_template {
    my ($self, $title, $content, $show_atom_link, $source_filename) = @_;

    my $menu = $self->generate_menu($self->{input_path});
    my $template_pathname = File::Spec->catfile($self->{template_path}, 'page.html.tpl');
    my $template_pathname = File::Spec->catfile($self->{template_path}, "page.$self->{template_type}.tpl");
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my $atom_url = File::Spec->catfile(dirname(File::Spec->abs2rel($source_filename, $self->{input_path})), "feed.xml");

    my $footer = '';
    my $footer_pathname = '';
    if (-f $source_filename) {
	$footer_pathname = File::Spec->catfile(dirname($source_filename), '.footer.html');
	$footer_pathname = File::Spec->catfile(dirname($source_filename), ".footer.$self->{template_type}");
    } elsif (-d $source_filename) {
	$footer_pathname = File::Spec->catfile($source_filename, '.footer.html');
	$footer_pathname = File::Spec->catfile($source_filename, ".footer.$self->{template_type}");
    }
    if (-f $footer_pathname) {
	$footer = read_file($footer_pathname);


@@ 75,15 75,22 @@ sub wrap_page_template {

    my $atom_feed = '';
    if ($show_atom_link) {
	$atom_feed = "<a href='$atom_url'><img class='feed-logo' src='/.static/images/atom.png' alt='Atom Feed'></a>";
	my $template_type = $self->{template_type};
	given ($template_type) {
	    when ('html') { $atom_feed = "<a href='$atom_url'><img class='feed-logo' src='/.static/images/atom.png' alt='Atom Feed'></a>"; }
	    when ('gmi')  { $atom_feed = "=> $atom_url Atom Feed\n"; }
	    default       { die "Unsupported template type"; }
	}
    }

    my @stylesheets = [];
    my $custom_css_pathname = File::Spec->catfile($self->{input_path}, ".static/styles/custom.css");
    if (-f $custom_css_pathname) {
	@stylesheets = ['halp.css', 'custom.css'];
    } else {
	@stylesheets = ['halp.css'];
    if ($self->{include_stylesheets}) {
	my $custom_css_pathname = File::Spec->catfile($self->{input_path}, ".static/styles/custom.css");
	if (-f $custom_css_pathname) {
	    @stylesheets = ['halp.css', 'custom.css'];
	} else {
	    @stylesheets = ['halp.css'];
	}
    }

    my $result = $template->fill_in(HASH => {'title' => $title, 'content' => $content, 'menu' => $menu, 'atom_feed' => $atom_feed, 'stylesheets' => @stylesheets, 'footer' => $footer});


@@ 100,7 107,7 @@ sub generate_file {
    my ($extension) = $source_file =~ /\.([^.]*)$/;
    my $content = read_file($source_file, binmode => ':bytes');

    if ($extension eq "html") {
    if ($extension eq "$self->{template_type}") {
	$content = $self->wrap_page_template(file_title($source_file), $content, 0, $source_file);
    }



@@ 123,14 130,15 @@ sub generate_directory {
    my $title = directory_title($source_path);

    my $directory_description = "";
    my $description_pathname = File::Spec->catfile($source_path, '.description.html');
    my $description_pathname = File::Spec->catfile($source_path, ".description.$self->{template_type}");
    if (-f $description_pathname) {
	$directory_description = read_file($description_pathname);
	$directory_description =~ s/\s+$//;
    }

    if ($relative_path !~ /^\..+$/) {
	my $template_pathname = File::Spec->catfile($self->{template_path}, 'directory_listing.html.tpl');
	my $template_pathname = File::Spec->catfile($self->{template_path}, "directory_listing.$self->{template_type}.tpl");
	print "$template_pathname\n";
	my $template = Text::Template->new(SOURCE => $template_pathname);
	my @items = directory_listing($self->{input_path}, $source_path, 0);
	my $result = $template->fill_in(HASH => {'path' => $source_path,


@@ 138,7 146,7 @@ sub generate_directory {
						     'items' => \@items});
	$result = $self->wrap_page_template($title, $result, 1, $source_path);

	my $index_file = File::Spec->catfile($output_path, "index.html");
	my $index_file = File::Spec->catfile($output_path, "index.$self->{template_type}");
	open my $file, '>', $index_file or die "Can't open '$index_file'";
	print $file $result;
	close $file;


@@ 169,6 177,9 @@ sub generate_directory {
sub generate {
    my ($self) = @_;

    if (!-d $self->{default_site_path}) { die "Can't find default site path $self->{default_site_path}"; }
    if (!-d $self->{output_path}) { die "Can't find output path $self->{output_path}"; }

    dircopy($self->{default_site_path}, $self->{output_path}) or die $!;
    $self->generate_directory($self->{input_path});
}

M t/Halp/generation_test.t => t/Halp/generation_test.t +2 -0
@@ 10,6 10,8 @@ use Test::More;

use Test::More tests => 4;

system("git clean -fdx ./tmp");

my $result = system("perl ./halp.pl --config-filename ./dev-config/halp-config.pl --web");
ok($result == 0, "halp --web executed");


A t/expected/gemini/.gitkeep => t/expected/gemini/.gitkeep +0 -0
A t/expected/www/.gitkeep => t/expected/www/.gitkeep +0 -0
A tmp/gemini/.gitkeep => tmp/gemini/.gitkeep +0 -0
A tmp/www/.gitkeep => tmp/www/.gitkeep +0 -0