7 has pages => (is => 'lazy');
9 has filters => (is => 'lazy');
11 has _layout_zoom => (is => 'lazy');
13 has _feed_configs => (is => 'lazy');
15 has _feed_generator => (
17 handles => { _feed_http_response => 'feed_http_response' },
22 pages_dir => 'share/pages',
23 template_dir => 'share/templates',
24 feed_id_prefix => 'http://shadow.cat',
30 SCSite::PageSet->new(base_dir => io->dir($self->config->{pages_dir}))
35 require SCSite::SubListFilter;
36 require SCSite::SidebarFilter;
38 map +($_ => "SCSite::${_}Filter"->new_from_site($self)),
43 sub _build__feed_configs {
46 title => 'All Shadowcat blogs',
47 entries => { min_depth => 2, max_depth => 3 },
49 'blog/matt-s-trout' => {
50 title => q{Matt S Trout (mst)'s blog},
51 entries => { at_depth => 1 },
53 'blog/mark-keating' => {
54 title => q{Mark Keating (mdk)'s blog},
55 entries => { min_depth => 1, max_depth => 2 },
58 title => 'Shadowcat News',
59 entries => { at_depth => 4 },
62 $f->{$_}{base} ||= $_ for keys %$f;
66 sub _build__feed_generator {
68 require SCSite::FeedGenerator;
69 SCSite::FeedGenerator->new(
70 pages => $self->pages,
71 id_prefix => $self->config->{feed_id_prefix},
75 sub dispatch_request {
78 if (my $conf = $self->_feed_configs->{$_[1]}) {
79 $self->_feed_http_response(200 => $conf);
83 $self->_page_http_response(200 => $self->_find_page('index'));
86 [ 302, [ 'Location' => "/$_[1]/" ], [] ]
89 my ($code, $page) = map {
90 $_ ? (200, $_) : (404, $self->_error_page(404))
91 } $self->_find_page($_[1]);
92 $self->_page_http_response($code => $page);
97 my ($self, $code) = @_;
98 $self->_find_page("error_${code}");
102 my ($self, $path) = @_;
103 $self->pages->get({ path => $path });
107 my ($self, $code, $type, $content) = @_;
108 [ $code, [ 'Content-type' => $type ], [ $content ] ];
111 sub _page_http_response {
112 my ($self, $code, $page) = @_;
113 [ $code, [ 'Content-type' => 'text/html' ], $self->_render_page($page) ];
117 my ($self, $page) = @_;
118 my $zoom = $self->_layout_zoom;
119 my %filters = %{$self->filters};
120 $zoom->select('.page.title')->replace_content($page->title)
121 ->select('meta[name=description]')->replace_content($page->description)
122 ->select('meta[name=keywords]')->replace_content($page->keywords)
123 ->select('.main')->replace_content(\$page->body)
125 foreach my $fname (sort keys %filters) {
126 my $cb = $filters{$fname}->callback_for($page);
127 $_ = $_->select(".${fname}")->collect({
128 filter => $cb, passthrough => 1
136 sub _build__layout_zoom {
138 HTML::Zoom->from_file(
139 io->dir($self->config->{template_dir})->catfile('layout.html')
144 return $_[0]->to_psgi_app if caller(1);
146 my @config_keys = keys %{{$class->default_config}};
147 require Getopt::Long;
149 Getopt::Long::GetOptions(
150 map +("$_=s" => \$config{$_}), @config_keys
152 delete $config{$_} for grep !defined($config{$_}), keys %config;
153 my $new = $class->new(config => \%config);
157 __PACKAGE__->run_if_script;