Commit | Line | Data |
e30ed59d |
1 | package App::IdiotBox; |
2 | |
3 | use Web::Simple __PACKAGE__; |
4 | use Method::Signatures::Simple; |
d7497a23 |
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} } |
d9702c6d |
19 | sub video_count { |
20 | exists $_[0]->{video_count} |
21 | ? $_[0]->{video_count} |
22 | : $_[0]->{videos}->count |
23 | } |
71fd1550 |
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} } |
d7497a23 |
33 | } |
34 | |
35 | default_config( |
71a02d85 |
36 | template_dir => 'share/html', |
37 | store => 'SQLite', |
38 | db_file => 'var/lib/idiotbox.db', |
d7497a23 |
39 | ); |
e30ed59d |
40 | |
71a02d85 |
41 | sub BUILD { |
42 | my $self = shift; |
43 | my $store; |
44 | ($store = $self->config->{store}) =~ /^(\w+)$/ |
45 | or die "Store config should be just a name, got ${store} instead"; |
46 | my $store_class = "App::IdiotBox::Store::${store}"; |
47 | eval "require ${store_class}; 1" |
48 | or die "Couldn't load ${store} store: $@"; |
49 | $store_class->bind($self); |
50 | } |
51 | |
e30ed59d |
52 | dispatch { |
53 | sub (/) { $self->show_front_page }, |
54 | subdispatch sub (/*/...) { |
55 | my $bucket = $self->buckets->get({ slug => $_[1] }); |
56 | [ |
57 | sub (/) { |
58 | $self->show_bucket($bucket) |
59 | }, |
71a02d85 |
60 | sub (/*/) { |
d7497a23 |
61 | $self->show_video($bucket->videos->get({ slug => $_[1] })); |
e30ed59d |
62 | } |
63 | ] |
64 | } |
65 | }; |
66 | |
d7497a23 |
67 | method recent_announcements { $self->{recent_announcements} } |
68 | |
71a02d85 |
69 | method buckets { $self->{buckets} } |
70 | |
e30ed59d |
71 | method show_front_page { |
72 | my $ann = $self->recent_announcements; |
73 | $self->html_response( |
1a1c4f78 |
74 | front_page => sub { |
75 | $_->select('#announcement-list') |
76 | ->repeat_content($ann->map(sub { |
77 | my $obj = $_; |
78 | sub { |
79 | $_->select('.bucket-name')->replace_content($obj->bucket->name) |
80 | ->select('.bucket-link')->set_attribute({ |
81 | name => 'href', value => $obj->bucket->slug.'/' |
82 | }) |
83 | ->select('.new-videos')->replace_content($obj->video_count) |
84 | ->select('.total-videos')->replace_content( |
85 | $obj->bucket->video_count |
86 | ) |
87 | } |
88 | })) |
89 | } |
e30ed59d |
90 | ); |
91 | } |
92 | |
d7497a23 |
93 | method show_bucket ($bucket) { |
1a1c4f78 |
94 | $self->html_response(bucket => sub { |
95 | $_->select('.bucket-name')->replace_content($bucket->name) |
96 | ->select('#video-list')->repeat_content($bucket->videos->map(sub { |
97 | my $video = $_; |
98 | sub { |
99 | $_->select('.video-name')->replace_content($video->name) |
100 | ->select('.video-author')->replace_content($video->author) |
101 | ->select('.video-link')->set_attribute( |
102 | { name => 'href', value => $video->slug.'/' } |
103 | ) |
104 | } |
105 | })) |
106 | }); |
d7497a23 |
107 | } |
108 | |
109 | method show_video ($video) { |
1a1c4f78 |
110 | $self->html_response(video => sub { |
111 | $_->select('.video-name')->replace_content($video->name) |
112 | ->select('.author-name')->replace_content($video->author) |
113 | ->select('.bucket-link')->set_attribute( |
114 | { name => 'href', value => '../' } |
115 | ) |
116 | ->select('.bucket-name')->replace_content($video->bucket->name) |
117 | ->select('.video-details')->replace_content($video->details) |
118 | }); |
d7497a23 |
119 | } |
120 | |
e30ed59d |
121 | method html_response ($template_name, $selectors) { |
1a1c4f78 |
122 | my $io = $self->_zoom_for($template_name => $selectors)->to_fh; |
e30ed59d |
123 | return [ 200, [ 'Content-Type' => 'text/html' ], $io ] |
124 | } |
125 | |
d7497a23 |
126 | method _template_filename_for ($name) { |
127 | $self->{config}{template_dir}.'/'.$name.'.html'; |
128 | } |
129 | |
e30ed59d |
130 | method _layout_zoom { |
d7497a23 |
131 | $self->{layout_zoom} ||= HTML::Zoom->from_file( |
132 | $self->_template_filename_for('layout') |
e30ed59d |
133 | ) |
134 | } |
135 | |
136 | method _zoom_for ($template_name, $selectors) { |
137 | ($self->{zoom_for_template}{$template_name} ||= do { |
138 | my @body; |
d7497a23 |
139 | HTML::Zoom->from_file( |
140 | $self->_template_filename_for($template_name) |
e30ed59d |
141 | ) |
1a1c4f78 |
142 | ->select('#main-content')->collect_content({ into => \@body }) |
d7497a23 |
143 | ->run; |
1a1c4f78 |
144 | $self->_layout_zoom |
145 | ->select('#main-content')->replace_content(\@body) |
146 | ->memoize; |
147 | })->apply($selectors); |
e30ed59d |
148 | } |
149 | |
150 | 1; |