$page->body, custom plugin loading
[scpubgit/App-SCS.git] / lib / App / SCS / Page.pm
1 package App::SCS::Page;
2
3 use IO::All;
4 use Time::Local qw(timelocal);
5 use Data::Pond qw(pond_read_datum pond_write_datum);
6 use List::Util qw(reduce);
7 use Module::Runtime qw(use_module);
8 use HTML::Zoom;
9 use Moo;
10
11 has "_$_" => (is => 'ro', init_arg => $_) for qw(page_set);
12
13 sub _page_set_class { ref($_[0]->_page_set) }
14 sub _top_dir { $_[0]->_page_set->top_dir }
15 sub _my_path { io->dir($_[0]->_top_dir)->catdir($_[0]->path) }
16
17 with 'App::SCS::Role::PageChildren';
18
19 has $_ => (is => 'ro') for qw(
20   title subtitle description keywords plugins html created path
21 );
22
23 has plugin_config => (is => 'lazy');
24
25 sub _build_plugin_config {
26   my ($self) = @_;
27   $self->plugins
28     ? pond_read_datum('[ '.$self->plugins.' ]')
29     : []
30 }
31
32 sub has_plugin_config { exists $_[0]->plugin_config->{$_[1]} }
33
34 sub with_plugin_config {
35   my ($self, $with_name, $with_config) = @_;
36   my @orig = @{$self->plugin_config};
37   my @new;
38   while (my ($name, $config) = splice @orig, 0, 2) {
39     push @new, (
40       $name eq $with_name
41         ? ($name, { %$config, %$with_config })
42         : ($name, $config)
43     );
44   }
45   return $self->with(plugins => pond_write_datum(\@new));
46 }
47
48 has _page_plugins => (is => 'lazy');
49
50 sub _build__page_plugins {
51   my ($self) = @_;
52   my $plugin_config = $self->plugin_config;
53   my ($plugin_map, $defaults) = @{$self->_page_set->plugin_config}
54                                  {qw(plugin_map defaults)};
55   my @spec = (@$defaults, @$plugin_config);
56   my @plugins;
57   while (my ($name, $config) = splice @spec, 0, 2) {
58     my $info = $plugin_map->{$name};
59     push @plugins,
60       use_module($info->{class})->new(
61         ($info->{config}||sub{})->(), %$config, page => $self
62       );
63   }
64   return \@plugins;
65 }
66
67 sub published_at {
68   $_[0]->created
69     ? scalar localtime timelocal
70         map +(@{$_}[0..3], $_->[4]-1, $_->[5]-1900),
71           [ reverse split '\D+', $_[0]->created ]
72     : ''
73 }
74
75 sub to_app {
76   my ($self) = @_;
77   return sub { $self->to_psgi_response(@_) };
78 }
79
80 sub to_psgi_response {
81   my ($self, $env) = @_;
82
83   if (my $cb = $env->{'App::SCS::Command::Generate.extra_pages'}) {
84     $cb->($_->extra_pages) for @{$self->page_plugins};
85   }
86
87   $self->_psgi_response;
88 }
89
90 has _psgi_response => (is => 'lazy');
91
92 sub _build__psgi_response {
93   my ($self) = @_;
94
95   my $psgi_res = [
96     200, [ 'Content-type' => 'text/html' ], $self->_content_zoom->to_fh
97   ];
98
99   return reduce {
100     $b->filter_psgi_response($a)
101   } $psgi_res, @{$self->_page_plugins};
102 }
103
104 sub _content_zoom {
105   my ($self) = @_;
106   return reduce {
107     $b->filter_content_zoom($a)
108   } $self->_html_zoom, @{$self->_page_plugins};
109 }
110
111 sub _html_zoom {
112   my ($self) = @_;
113   return reduce {
114     $b->filter_html_zoom($a)
115   } HTML::Zoom->from_html($self->html), @{$self->_page_plugins};
116 }
117
118 sub body {
119   my ($self) = @_;
120   HTML::Zoom->from_html($self->html)
121             ->collect(body => { into => \my @ev })
122             ->run;
123   HTML::Zoom->from_events(\@ev)->to_html;
124 }
125
126 no Moo;
127
128 sub with {
129   my $self = shift;
130   return ref($self)->new(%$self, @_);
131 }
132
133 1;