use Test::Needs
[catagits/Web-Simple.git] / examples / bloggery / bloggery.cgi
1 #!/usr/bin/perl
2
3 use FindBin;
4 use lib $FindBin::Bin.'/code';
5 use Web::Simple 'Bloggery';
6
7 package Bloggery::PostList;
8
9 use File::stat;
10
11 sub from_dir {
12   my ($class, $dir) = @_;
13   bless ({ dir => $dir }, $class);
14 }
15
16 sub all {
17   my ($self) = @_;
18   map { Bloggery::Post->from_file($_) }
19     sort { stat($a)->mtime <=> stat($b)->mtime }
20     grep { !/\.summary\.html$/ }
21       glob($self->{dir}.'/*.html');
22 }
23
24 sub post {
25   my ($self, $name) = @_;
26   my $file = $self->{dir}."/${name}.html";
27   return unless $file && -f $file;
28   return Bloggery::Post->from_file($file);
29 }
30
31 sub map {
32   my ($self, $code) = @_;
33   map $code->($_), $self->all;
34 }
35
36 package Bloggery::Post;
37
38 sub from_file {
39   my ($class, $file) = @_;
40   bless({ file => $file }, $class);
41 }
42
43 sub name {
44   my $name = shift->{file};
45   $name =~ s/.*\///;
46   $name =~ s/\.html$//;
47   $name;
48 }
49
50 sub title {
51   my $title = shift->name;
52   $title =~ s/-/ /g;
53   $title;
54 }
55
56 sub html {
57   \do { local (@ARGV, $/) = shift->{file}; <> };
58 }
59
60 sub summary_html {
61   my $file = shift->{file};
62   $file =~ s/\.html$/.summary.html/;
63   return \'<p>No summary</p>' unless -f $file;
64   \do { local (@ARGV, $/) = $file; <> };
65 }
66
67 package Bloggery;
68
69 has post_list => (is => 'lazy');
70
71 sub default_config {
72   (
73     title => 'Bloggery',
74     posts_dir => $FindBin::Bin.'/posts',
75   );
76 }
77
78 sub _build_post_list {
79   my ($self) = @_;
80   Bloggery::PostList->from_dir(
81     $self->config->{posts_dir}
82   );
83 }
84
85 sub post {
86   my ($self, $post) = @_;
87   $self->post_list->post($post);
88 }
89
90 sub dispatch_request {
91   my $self = shift;
92   sub (GET + /) {
93     redispatch_to '/index.html'
94   },
95   sub (.html) {
96     response_filter { $self->render_html(@_) }
97   },
98   sub (GET + /index) {
99     $self->post_list
100   },
101   sub (GET + /*) {
102     $self->post($_[1])
103   },
104   sub (GET) {
105     [ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ]
106   },
107   sub {
108     [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
109   },
110 };
111
112 sub render_html {
113   my ($self, $data) = @_;
114   use HTML::Tags;
115   return $data if ref($data) eq 'ARRAY';
116   return [
117     200,
118     [ 'Content-type', 'text/html' ],
119     [
120       HTML::Tags::to_html_string(
121         <html>,
122           <head>,
123             <title>, $self->title_for($data), </title>,
124           </head>,
125           <body>,
126             <h1>, $self->title_for($data), </h1>,
127             <div id="main">,
128               $self->main_html_for($data),
129             </div>,
130           </body>,
131         </html>
132       )
133     ]
134   ];
135 }
136
137 sub title_for {
138   my ($self, $data) = @_;
139   if ($data->isa('Bloggery::Post')) {
140     return $data->title;
141   }
142   return $self->config->{title};
143 }
144
145 sub main_html_for {
146   my ($self, $data) = @_;
147   use HTML::Tags;
148   if ($data->isa('Bloggery::Post')) {
149     $data->html
150   } elsif ($data->isa('Bloggery::PostList')) {
151     <ul>,
152       $data->map(sub {
153         my $path = $_->name.'.html';
154         <li>,
155           <h4>, <a href="$path">, $_->title, </a>, </h4>,
156           <span class="summary">, $_->summary_html, </span>,
157         </li>;
158       }),
159     </ul>;
160   } else {
161     <h2>, "Don't know how to render $data", </h2>;
162   }
163 }
164
165 Bloggery->run_if_script;