Commit | Line | Data |
dac1c02b |
1 | #!/usr/bin/env perl |
2 | |
5cc6d9e2 |
3 | package SCSite; |
4 | |
2f796f69 |
5 | our $VERSION = '0.001001'; # 0.1.1 |
6 | |
5cc6d9e2 |
7 | use IO::All; |
8 | use SCSite::PageSet; |
9 | use Web::Simple; |
10 | |
5cc6d9e2 |
11 | has pages => (is => 'lazy'); |
12 | |
ebd4c292 |
13 | has filters => (is => 'lazy'); |
14 | |
5cc6d9e2 |
15 | has _layout_zoom => (is => 'lazy'); |
16 | |
221c4151 |
17 | has _feed_configs => (is => 'lazy'); |
18 | |
19 | has _feed_generator => ( |
20 | is => 'lazy', |
21 | handles => { _feed_http_response => 'feed_http_response' }, |
22 | ); |
23 | |
5cc6d9e2 |
24 | sub default_config { |
25 | ( |
5ca56460 |
26 | pages_dir => 'share/pages', |
27 | template_dir => 'share/templates', |
99cee940 |
28 | static_dir => 'share/static', |
da140bff |
29 | generate_host => 'www.example.com', |
5cc6d9e2 |
30 | ) |
31 | } |
32 | |
33 | sub _build_pages { |
34 | my ($self) = @_; |
ebd4c292 |
35 | SCSite::PageSet->new(base_dir => io->dir($self->config->{pages_dir})) |
36 | } |
37 | |
38 | sub _build_filters { |
39 | my ($self) = @_; |
fc436d2e |
40 | +{ |
da140bff |
41 | map { |
42 | require "SCSite/${_}Filter.pm"; |
43 | +($_ => "SCSite::${_}Filter"->new_from_site($self)) |
44 | } @{$self->config->{filters}} |
fc436d2e |
45 | } |
5cc6d9e2 |
46 | } |
47 | |
221c4151 |
48 | sub _build__feed_configs { |
da140bff |
49 | my ($self) = @_; |
50 | my $f = $self->config->{feeds}||{}; |
221c4151 |
51 | $f->{$_}{base} ||= $_ for keys %$f; |
52 | $f; |
53 | } |
54 | |
55 | sub _build__feed_generator { |
56 | my ($self) = @_; |
57 | require SCSite::FeedGenerator; |
58 | SCSite::FeedGenerator->new( |
59 | pages => $self->pages, |
221c4151 |
60 | ); |
61 | } |
62 | |
5cc6d9e2 |
63 | sub dispatch_request { |
64 | my $self = shift; |
65 | sub (/feed/**/) { |
221c4151 |
66 | if (my $conf = $self->_feed_configs->{$_[1]}) { |
da140bff |
67 | $self->_feed_http_response(200 => $conf => $_[PSGI_ENV]); |
221c4151 |
68 | } |
5cc6d9e2 |
69 | }, |
70 | sub (/) { |
71 | $self->_page_http_response(200 => $self->_find_page('index')); |
72 | }, |
73 | sub (/**) { |
74 | [ 302, [ 'Location' => "/$_[1]/" ], [] ] |
75 | }, |
76 | sub (/**/) { |
77 | my ($code, $page) = map { |
78 | $_ ? (200, $_) : (404, $self->_error_page(404)) |
79 | } $self->_find_page($_[1]); |
80 | $self->_page_http_response($code => $page); |
81 | } |
82 | } |
83 | |
84 | sub _error_page { |
85 | my ($self, $code) = @_; |
86 | $self->_find_page("error_${code}"); |
87 | } |
88 | |
89 | sub _find_page { |
90 | my ($self, $path) = @_; |
91 | $self->pages->get({ path => $path }); |
92 | } |
93 | |
94 | sub _http_response { |
95 | my ($self, $code, $type, $content) = @_; |
96 | [ $code, [ 'Content-type' => $type ], [ $content ] ]; |
97 | } |
98 | |
99 | sub _page_http_response { |
100 | my ($self, $code, $page) = @_; |
101 | [ $code, [ 'Content-type' => 'text/html' ], $self->_render_page($page) ]; |
102 | } |
103 | |
104 | sub _render_page { |
105 | my ($self, $page) = @_; |
106 | my $zoom = $self->_layout_zoom; |
fc436d2e |
107 | my %filters = %{$self->filters}; |
108 | $zoom->select('.page.title')->replace_content($page->title) |
2a19b2a6 |
109 | ->select('.page.subtitle')->${\sub { |
110 | $page->subtitle |
111 | ? $_[0]->replace_content($page->subtitle) |
112 | : $_[0]->replace('') |
113 | }} |
114 | ->select('.page.published_at')->replace_content($page->published_at) |
99cee940 |
115 | ->select('meta[name=description]') |
116 | ->set_attribute(content => $page->description) |
117 | ->select('meta[name=keywords]') |
118 | ->set_attribute(content => $page->keywords) |
2a19b2a6 |
119 | ->select('meta[name=created]') |
120 | ->set_attribute(content => $page->created) |
121 | ->select('.page.body')->replace_content(\$page->body) |
fc436d2e |
122 | ->apply(sub { |
123 | foreach my $fname (sort keys %filters) { |
124 | my $cb = $filters{$fname}->callback_for($page); |
125 | $_ = $_->select(".${fname}")->collect({ |
126 | filter => $cb, passthrough => 1 |
127 | }); |
128 | } |
129 | $_ |
ebd4c292 |
130 | }) |
5cc6d9e2 |
131 | ->to_fh |
132 | } |
133 | |
134 | sub _build__layout_zoom { |
135 | my ($self) = @_; |
136 | HTML::Zoom->from_file( |
137 | io->dir($self->config->{template_dir})->catfile('layout.html') |
138 | )->memoize; |
139 | } |
140 | |
141 | sub run_if_script { |
142 | return $_[0]->to_psgi_app if caller(1); |
da140bff |
143 | my $self = ref($_[0]) ? $_[0] : $_[0]->new; |
144 | my @config_keys = keys %{{$self->default_config}}; |
5cc6d9e2 |
145 | require Getopt::Long; |
f82adbe1 |
146 | my %config = map +($_ => $ENV{"SCS_${\uc $_}"}), @config_keys; |
5cc6d9e2 |
147 | Getopt::Long::GetOptions( |
148 | map +("$_=s" => \$config{$_}), @config_keys |
149 | ); |
150 | delete $config{$_} for grep !defined($config{$_}), keys %config; |
da140bff |
151 | @{$self->config}{keys %config} = values %config; |
152 | $self->run(@_) |
5cc6d9e2 |
153 | } |
154 | |
99cee940 |
155 | around _run_cli => sub { |
156 | my ($orig, $self) = (shift, shift); |
7857f551 |
157 | if (@_ >= 2 and $_[0] eq 'dev') { |
99cee940 |
158 | require SCSite::DevMode; |
159 | Moo::Role->apply_roles_to_object($self, 'SCSite::DevMode'); |
7857f551 |
160 | if ($self->can("_run_dev_$_[1]")) { |
161 | return $self->${\"_run_dev_$_[1]"}(@_[2..$#_]); |
162 | } else { |
163 | die "No such dev mode $_[1]"; |
164 | } |
165 | } |
166 | if (@_ >= 1 and my $code = $self->can("_run_cli_$_[0]")) { |
167 | shift; |
168 | return $self->$code(@_); |
99cee940 |
169 | } |
170 | return $self->$orig(@_); |
171 | }; |
172 | |
7857f551 |
173 | sub _run_cli_generate { |
58152655 |
174 | my ($self, $to, @spec) = @_; |
7857f551 |
175 | die "generate requires a directory to generate to" |
176 | unless $to and -d $to; |
177 | my $out = io($to); |
58152655 |
178 | my $check = do { |
179 | if (@spec) { '^('.join('|',map quotemeta($_),@spec).')' } |
180 | else { '.' } |
181 | }; |
da140bff |
182 | my $prefix = 'http://'.$self->config->{generate_host}; |
7857f551 |
183 | foreach my $path ('', $self->pages->all_paths) { |
58152655 |
184 | next unless "$path/" =~ /$check/; |
da140bff |
185 | print "Generating page ${path}\n"; |
7857f551 |
186 | my $dir = $out->catdir($path); |
187 | $dir->mkpath; |
188 | $dir->catfile('index.html')->print( |
da140bff |
189 | $self->run_test_request(GET => "${prefix}${path}/")->content |
7857f551 |
190 | ); |
191 | } |
192 | foreach my $path (map "/feed/$_/", keys %{$self->_feed_configs}) { |
58152655 |
193 | next unless "$path/" =~ /$check/; |
da140bff |
194 | print "Generating feed ${path}\n"; |
7857f551 |
195 | my $dir = $out->catdir($path); |
196 | $dir->mkpath; |
197 | $dir->catfile('index.atom')->print( |
da140bff |
198 | $self->run_test_request(GET => "${prefix}${path}")->content |
7857f551 |
199 | ); |
200 | } |
201 | } |
202 | |
203 | |
5cc6d9e2 |
204 | __PACKAGE__->run_if_script; |