947180f9d2cef2731d5d73a2a18eb6dcefc28856
[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 default_config(
70   title => 'Bloggery',
71   posts_dir => $FindBin::Bin.'/posts',
72 );
73
74 sub post_list {
75   my ($self) = @_;
76   $self->{post_list}
77     ||= Bloggery::PostList->from_dir(
78           $self->config->{posts_dir}
79         );
80 }
81
82 sub post {
83   my ($self, $post) = @_;
84   $self->post_list->post($post);
85 }
86
87 dispatch [
88   sub (.html) {
89     response_filter { $self->render_html($_[1]) },
90   },
91   sub (GET + /) {
92     redispatch_to '/index.html';
93   },
94   sub (GET + /index) {
95     $self->post_list
96   },
97   sub (GET + /*) {
98     $self->post($_[1]);
99   },
100   sub (GET) {
101     [ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ]
102   },
103   sub {
104     [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
105   },
106 ];
107
108 sub render_html {
109   my ($self, $data) = @_;
110   use HTML::Tags;
111   return $data if ref($data) eq 'ARRAY';
112   return [
113     200,
114     [ 'Content-type', 'text/html' ],
115     [
116       HTML::Tags::to_html_string(
117         <html>,
118           <head>,
119             <title>, $self->title_for($data), </title>,
120           </head>,
121           <body>,
122             <h1>, $self->title_for($data), </h1>,
123             <div id="main">,
124               $self->main_html_for($data),
125             </div>,
126           </body>,
127         </html>
128       )
129     ]
130   ];
131 }
132
133 sub title_for {
134   my ($self, $data) = @_;
135   if ($data->isa('Bloggery::Post')) {
136     return $data->title;
137   }
138   return $self->config->{title};
139 }
140
141 sub main_html_for {
142   my ($self, $data) = @_;
143   use HTML::Tags;
144   if ($data->isa('Bloggery::Post')) {
145     $data->html
146   } elsif ($data->isa('Bloggery::PostList')) {
147     <ul>,
148       $data->map(sub {
149         my $path = $_->name.'.html';
150         <li>,
151           <h4>, <a href="$path">, $_->title, </a>, </h4>,
152           <span class="summary">, $_->summary_html, </span>,
153         </li>;
154       }),
155     </ul>;
156   } else {
157     <h2>, "Don't know how to render $data", </h2>;
158   }
159 }
160
161 Bloggery->run_if_script;