sort of works with store
[catagits/App-IdiotBox.git] / lib / App / IdiotBox.pm
1 package App::IdiotBox;
2
3 use Web::Simple __PACKAGE__;
4 use Method::Signatures::Simple;
5 use FindBin;
6 use HTML::Zoom;
7
8 {
9   package App::IdiotBox::Announcement;
10
11   sub made_at { shift->{made_at} } 
12   sub bucket { shift->{bucket} } 
13   sub video_count { shift->{video_count} } 
14
15   package App::IdiotBox::Bucket;
16
17   sub slug { shift->{slug} }
18   sub name { shift->{name} }
19   sub video_count {
20     exists $_[0]->{video_count}
21       ? $_[0]->{video_count}
22       : $_[0]->{videos}->count
23   }
24   sub videos { shift->{videos} }
25
26   package App::IdiotBox::Video;
27
28   sub slug { shift->{slug} }
29   sub name { shift->{name} }
30   sub author { shift->{author} }
31   sub details { shift->{details} }
32   sub bucket { shift->{bucket} }
33 }
34
35 default_config(
36   template_dir => $FindBin::Bin.'/../share/html'
37 );
38
39 dispatch {
40   sub (/) { $self->show_front_page },
41   subdispatch sub (/*/...) {
42     my $bucket = $self->buckets->get({ slug => $_[1] });
43     [
44       sub (/) {
45         $self->show_bucket($bucket)
46       },
47       sub (/*) {
48         $self->show_video($bucket->videos->get({ slug => $_[1] }));
49       }
50     ]
51   }
52 };
53
54 method recent_announcements { $self->{recent_announcements} }
55
56 method show_front_page {
57   my $ann = $self->recent_announcements;
58   $self->html_response(
59     front_page => [
60       '#announcement-list' => [
61         -repeat_content => {
62           repeat_for => $ann->map(sub { [
63             '.fill-bucket-name' => [
64               -replace_content => { replace_with => $_->bucket->name }
65             ],
66             '.fill-bucket-link' => [
67               -set_attribute => { name => 'href', value => $_->bucket->slug.'/' }
68             ],
69             '.fill-new-videos' => [
70               -replace_content => { replace_with => $_->video_count }
71             ],
72             '.fill-total-videos' => [
73               -replace_content => { replace_with => $_->bucket->video_count }
74             ],
75           ] })->as_stream
76         }
77       ]
78     ]
79   );
80 }
81
82 method show_bucket ($bucket) {
83   $self->html_response(bucket => [
84     '.fill-bucket-name' => [
85       -replace_content => { replace_with => $bucket->name }
86     ],
87     '#video-list' => [
88       -repeat_content => {
89         repeat_for => $bucket->videos->map(sub { [
90           '.fill-video-name' => [
91             -replace_content => { replace_with => $_->name }
92           ],
93           '.fill-video-author' => [
94             -replace_content => { replace_with => $_->author }
95           ],
96           '.fill-video-link' => [
97             -set_attribute => {
98               name => 'href', value => $_->slug.'/'
99             }
100           ],
101         ] })->as_stream
102       }
103     ]
104   ]);
105 }
106
107 method show_video ($video) {
108   $self->html_response(video => [
109     '.fill-video-name' => [
110       -replace_content => { replace_with => $video->name }
111     ],
112     '.fill-author-name' => [
113       -replace_content => { replace_with => $video->author }
114     ],
115     '.fill-bucket-link' => [
116       -set_attribute => { name => 'href', value => '../' }
117     ],
118     '.fill-bucket-name' => [
119       -replace_content => { replace_with => $video->bucket->name }
120     ],
121     '.fill-video-details' => [
122       -replace_content => { replace_with => $video->details }
123     ]
124   ]);
125 }
126
127 method html_response ($template_name, $selectors) {
128   my $io = $self->_zoom_for($template_name => $selectors)->as_readable_fh;
129   return [ 200, [ 'Content-Type' => 'text/html' ], $io ]
130 }
131
132 method _template_filename_for ($name) {
133   $self->{config}{template_dir}.'/'.$name.'.html';
134 }
135
136 method _layout_zoom {
137   $self->{layout_zoom} ||= HTML::Zoom->from_file(
138     $self->_template_filename_for('layout')
139   )
140 }
141
142 method _zoom_for ($template_name, $selectors) {
143   ($self->{zoom_for_template}{$template_name} ||= do {
144     my @body;
145     HTML::Zoom->from_file(
146                   $self->_template_filename_for($template_name)
147                 )
148               ->with_selectors(
149                   '#main-content' => [
150                     -capture_events => { into => \@body }
151                   ]
152                 )
153               ->run;
154     $self->_layout_zoom->with_selectors(
155       '#main-content' => [
156         -replace_content_events => { replace_with => \@body }
157       ]
158     )->to_zoom;
159   })->with_selectors($selectors)
160 }
161
162 1;