subtitle handling, prettified dates. bugfixes and better errors
[scpubgit/SCS.git] / lib / SCSite / PageSet.pm
CommitLineData
95148a72 1package SCSite::PageSet;
2
95148a72 3use IO::All;
4use Text::MultiMarkdown 'markdown';
5use HTML::Zoom;
ebd4c292 6use Sub::Quote;
7use Syntax::Keyword::Gather;
8use SCSite::Page;
95148a72 9use Moo;
2a19b2a6 10use Try::Tiny;
95148a72 11
34597fb2 12has top_dir => (is => 'ro', lazy => 1, builder => 'base_dir');
95148a72 13has base_dir => (is => 'ro', required => 1);
ebd4c292 14has max_depth => (is => 'ro', default => quote_sub q{ 0 });
221c4151 15has min_depth => (is => 'ro', default => quote_sub q{ 1 });
95148a72 16
34597fb2 17has rel_path => (is => 'lazy');
18
19sub _build_rel_path {
20 my ($self) = @_;
21 io->dir('/')
22 ->catdir(File::Spec->abs2rel($self->base_dir->name, $self->top_dir->name))
23}
24
95148a72 25sub get {
26 my ($self, $spec) = @_;
27 $spec->{path} or die "path is required to get";
28 my ($dir, $file) = $spec->{path} =~ m{^(?:(.*)/)?([^/]+)$};
29 my $type;
5cc6d9e2 30 my @poss = io->dir($self->base_dir)->${\sub {
95148a72 31 my $io = shift;
32 defined($dir) ? $io->catdir($dir) : $io
ebd4c292 33 }}->filter(sub {
34 $_->filename =~ /^\Q${file}\E${\$self->_types_re}$/ and $type = $1
35 })
221c4151 36 ->${\sub { -e "$_[0]" ? $_[0]->all_files : () }};
95148a72 37 die "multiple files found for ${\$spec->{path}}:\n".join "\n", @poss
38 if @poss > 1;
ebd4c292 39 return undef unless @poss;
2a19b2a6 40 $self->_inflate(
41 $type, $self->rel_path->catdir($spec->{path}), $poss[0]->all
34597fb2 42 );
ebd4c292 43}
44
2a19b2a6 45sub _inflate {
46 my ($self, $type, $path, $data) = @_;
47 try { $self->${\"_inflate_${type}"}($path, $data) }
48 catch {
49 die "Error inflating ${path} as ${type}: $_\n\nData was: ${data}";
50 }
51}
52
ebd4c292 53sub map {
54 my ($self, $mapper) = @_;
55 [ map $mapper->($_), $self->flatten ]
56}
57
fc436d2e 58sub _depth_under_base {
59 my ($self, $path) = @_;
60 File::Spec->splitdir(File::Spec->abs2rel($path, $self->base_dir->name))
61}
62
ebd4c292 63sub flatten {
64 my ($self) = @_;
fc436d2e 65 return unless (my $base = $self->base_dir)->exists;
ebd4c292 66 my %seen;
34597fb2 67 my $slash = io->dir('/');
fc436d2e 68 my $min = $self->min_depth;
ebd4c292 69 map {
70 my ($path, $type) = $_->name =~ /^(.*)${\$self->_types_re}$/;
2a19b2a6 71 $self->_inflate(
72 $type,
fc436d2e 73 $slash->catdir(File::Spec->abs2rel($path, $self->top_dir->name)),
74 $_->all
ebd4c292 75 );
fc436d2e 76 } map {
77 $_->filter(sub { $_->filename =~ /${\$self->_types_re}$/ })
221c4151 78 ->all_files($self->max_depth - ($min-1))
fc436d2e 79 } map
221c4151 80 $min > 1
fc436d2e 81 ? do {
221c4151 82 # can't use ->all_dirs($min-1) since we only want the final level
83 my @x = ($_); @x = map $_->all_dirs, @x for 1..$min-1; @x
fc436d2e 84 }
85 : $_,
221c4151 86 $base;
ebd4c292 87}
88
89sub latest {
90 my ($self, $max) = @_;
91 require SCSite::LatestPageSet;
92 SCSite::LatestPageSet->new(
93 parent => $self,
94 max_entries => $max,
95 );
95148a72 96}
97
98sub _new_page {
ebd4c292 99 SCSite::Page->new({ path => $_[1], page_set => $_[0], %{$_[2]} })
95148a72 100}
101
ebd4c292 102sub _types_re { qw/\.(html|md)/ }
103
95148a72 104sub _inflate_html {
ebd4c292 105 my ($self, $path, $html) = @_;
106 $self->_new_page($path, $self->_extract_from_html($html));
95148a72 107}
108
109sub _extract_from_html {
110 my ($self, $html) = @_;
111 HTML::Zoom->from_html($html)
112 ->select('title')->collect_content({ into => \my @title })
2a19b2a6 113 ->select('meta[name=subtitle]')->collect({ into => \my @subtitle })
95148a72 114 ->select('meta[name=description]')->collect({ into => \my @description })
115 ->select('meta[name=keywords]')->collect({ into => \my @keywords })
ebd4c292 116 ->select('meta[name=created]')->collect({ into => \my @created })
95148a72 117 ->select('body')->collect_content({ into => \my @body })
118 ->run;
119 +{
ebd4c292 120 title => $title[0]->{raw}||'',
2a19b2a6 121 subtitle => $subtitle[0]->{attrs}{content}||'',
ebd4c292 122 description => $description[0]->{attrs}{content}||'',
123 keywords => $keywords[0]->{attrs}{content}||'',
124 created => $created[0]->{attrs}{content}||'',
125 body => HTML::Zoom->from_events(\@body)->to_html||'',
95148a72 126 }
127}
128
129sub _inflate_md {
ebd4c292 130 my ($self, $path, $md) = @_;
131 $self->_new_page($path, $self->_extract_from_md($md));
95148a72 132}
133
134sub _extract_from_md {
135 my ($self, $md) = @_;
136 $self->_extract_from_html(markdown($md, { document_format => 'complete' }));
137}
138
1391;