File Coverage

blib/lib/YATT/Lite/WebMVC0/DirApp.pm
Criterion Covered Total %
statement 109 129 84.5
branch 40 64 62.5
condition 25 43 58.1
subroutine 22 27 81.4
pod 1 10 10.0
total 197 273 72.1


line stmt bran cond sub pod time code
1             package YATT::Lite::WebMVC0::DirApp; sub MY () {__PACKAGE__}
2 13     13   7796 use strict;
  13         32  
  13         386  
3 13     13   66 use warnings qw(FATAL all NONFATAL misc);
  13         30  
  13         423  
4 13     13   67 use Carp;
  13         36  
  13         731  
5 13     13   89 use mro 'c3';
  13         26  
  13         99  
6              
7 13         188 use YATT::Lite -as_base, qw/*SYS
8 13     13   446 Entity/;
  13         27  
9 13         69 use YATT::Lite::MFields qw/cf_dir_config
10             cf_use_subpath
11             cf_overwrite_status_code_for_errors_as
12              
13 13     13   91 Action/;
  13         75  
14              
15 13     13   6264 use YATT::Lite::WebMVC0::Connection;
  13         39  
  13         974  
16             sub Connection () {'YATT::Lite::WebMVC0::Connection'}
17             sub PROP () {Connection}
18              
19 13         928 use YATT::Lite::Util qw/cached_in ckeval
20             dofile_in compile_file_in
21             try_invoke
22             psgi_error
23             terse_dump
24 13     13   101 /;
  13         36  
25              
26 13     13   86 use YATT::Lite::Error;
  13         30  
  13         16314  
27              
28             # sub handle_ydo, _do, _psgi...
29              
30             sub handle {
31 178     178 0 567 (my MY $self, my ($type, $con, $file)) = @_;
32             chdir($self->{cf_dir})
33 178 50       3220 or die "Can't chdir '$self->{cf_dir}': $!";
34             local $SIG{__WARN__} = sub {
35 0     0   0 my ($msg) = @_;
36 0         0 die $self->raise(warn => $_[0]);
37 178         1722 };
38             local $SIG{__DIE__} = sub {
39 22     22   123 my ($err) = @_;
40 22 100       870 die $err if ref $err;
41 2         48 die $self->error({ignore_frame => [undef, __FILE__, __LINE__]}, $err);
42 178         1110 };
43 178 50       789 if (my $charset = $self->header_charset) {
44 178         852 $con->set_charset($charset);
45             }
46 178         864 $self->SUPER::handle($type, $con, $file);
47             }
48              
49             #
50             # WebMVC0 specific url mapping.
51             #
52             sub prepare_part_handler {
53 142     142 0 352 (my MY $self, my ($con, $file)) = @_;
54              
55 142         636 my $trans = $self->open_trans;
56              
57 142         507 my PROP $prop = $con->prop;
58              
59 142         321 my ($part, $sub, $pkg, @args);
60 142         650 my ($type, $item) = $self->parse_request_sigil($con);
61              
62 142 50 66     534 if (defined $type and my $subpath = $prop->{cf_subpath}) {
63 0 0       0 croak $self->error(q|Bad request: subpath %s and sigil %s|
64             , $subpath, terse_dump($type, $item))
65             if $type ne 'action';
66             }
67              
68 142 100 66     1201 if (not defined $type
      66        
69             and $self->{cf_use_subpath} and my $subpath = $prop->{cf_subpath}) {
70 75 50       282 my $tmpl = $trans->find_file($file) or do {
71 0         0 croak $self->error("No such file: %s", $file);
72             };
73 75 100       337 ($part, my ($formal, $actual)) = $tmpl->match_subroutes($subpath) or do {
74             # XXX: Is this secure against XSS? <- how about URI encoding?
75             # die $self->psgi_error(404, "No such subpath: ". $subpath);
76             die $self->psgi_error(404, "No such subpath:: ". $subpath
77 1         17 . " in file " . $tmpl->{cf_path});
78             };
79 74 50       369 $pkg = $trans->find_product(perl => $tmpl) or do {
80 0         0 croak $self->error("Can't compile template file: %s", $file);
81             };
82              
83 74 50       338 $sub = $pkg->can($part->method_name) or do {
84 0         0 croak $self->error("Can't find %s %s for file: %s"
85             , $part->cget('kind'), $part->public_name, $file);
86             };
87             @args = $part->reorder_cgi_params($con, $actual)
88 74 50       420 unless $self->{cf_dont_map_args};
89              
90             } else {
91 67         449 ($part, $sub, $pkg) = $trans->find_part_handler([$file, $type, $item]);
92              
93             @args = $part->reorder_cgi_params($con)
94 67 100 66     1547 unless $self->{cf_dont_map_args} || $part->isa($trans->Action);
95             }
96              
97 141 50       657 unless ($part->public) {
98             # XXX: refresh する手もあるだろう。
99 0         0 croak $self->error(q|Forbidden request %s|, $file);
100             }
101              
102 141         760 ($part, $sub, $pkg, \@args);
103             }
104              
105             #========================================
106             # Action handling
107             #========================================
108              
109             sub find_handler {
110 178     178 1 525 (my MY $self, my ($ext, $file, $con)) = @_;
111 178         586 my PROP $prop = $con->prop;
112 178 100       624 if ($prop->{cf_is_index}) {
113 91         392 my $sub_fn = substr($prop->{cf_path_info}, length($prop->{cf_location}));
114 91         277 $sub_fn =~ s,/.*,,;
115 91 100 100     2832 if ($sub_fn ne '' and my $action = $self->get_action_handler($sub_fn, 1)) {
116 30         105 return $action
117             }
118             }
119 148         700 $self->SUPER::find_handler($ext, $file, $con);
120             }
121              
122             sub _handle_ydo {
123 6     6   22 (my MY $self, my ($con, $file, @rest)) = @_;
124 6 50       24 my $action = $self->get_action_handler($file)
125             or die "Can't find action handler for file '$file'\n";
126              
127             # XXX: this は EntNS pkg か $YATT か...
128 6         41 $action->($self->EntNS, $con);
129             }
130              
131             # XXX: cached_in 周りは面倒過ぎる。
132             # XXX: package per dir で、本当に良いのか?
133             # XXX: Should handle union mount!
134              
135             #
136             sub get_action_handler {
137 69     69 0 214 (my MY $self, my ($filename, $can_be_missing)) = @_;
138 69         270 my $path = "$self->{cf_dir}/$filename";
139              
140             # Each action item is stored as:
141             # [$action_sub, $is_virtual, @more_opts..., $age_from_mtime]
142             #
143             my $item = $self->cached_in
144             ($self->{Action} //= {}, $path, $self, undef, sub {
145             # first time.
146 19     19   58 my ($self, $sys, $path) = @_;
147 19         470 my $age = -M $path;
148 19 100 66     178 return undef if not defined $age and $can_be_missing;
149 2         12 my $sub = compile_file_in(ref $self, $path);
150             # is not virtual.
151 2         10 [$sub, 0, $age];
152             }, sub {
153             # second time
154 34     34   100 my ($item, $sys, $path) = @_;
155 34         83 my ($sub, $age);
156 34 50       236 if (not defined $item) {
    100          
    50          
    100          
157             # XXX: (Accidental) negative cache. Is this ok?
158 0         0 return;
159             } elsif ($item->[1]) {
160             # return $action_sub without examining $path when item is virtual.
161 30         109 return $item->[0];
162             } elsif (not defined ($age = -M $path)) {
163             # item is removed from filesystem, so undef $sub.
164             } elsif ($$item[-1] == $age) {
165 3         14 return;
166             } else {
167 1         12 $sub = compile_file_in($self->{cf_app_ns}, $path);
168             }
169 1         5 @{$item}[0, -1] = ($sub, $age);
  1         9  
170 69   100     922 });
171 69 100 66     1034 return unless defined $item and $item->[0];
172 36 50       248 wantarray ? @$item : $item->[0];
173             }
174              
175             sub set_action_handler {
176 30     30 0 106 (my MY $self, my ($filename, $sub)) = @_;
177              
178 30         152 $filename =~ s,^/*,,;
179              
180 30         135 my $path = "$self->{cf_dir}/$filename";
181              
182 30         153 $self->{Action}{$path} = [$sub, 1, undef];
183             }
184              
185             #========================================
186             # Response Header
187             #========================================
188              
189 0     0 0 0 sub default_header_charset {''}
190             sub header_charset {
191 178     178 0 470 (my MY $self) = @_;
192             $self->{cf_header_charset} || $self->{cf_output_encoding}
193 178 0 33     1076 || $SYS->header_charset
      0        
194             || $self->default_header_charset;
195             }
196              
197             #========================================
198              
199             sub get_lang_msg {
200 0     0 0 0 (my MY $self, my $lang) = @_;
201 0 0       0 $self->{locale_cache}{$lang} || do {
202 0 0       0 if (-r (my $fn = $self->fn_msgfile($lang))) {
203 0         0 $self->lang_load_msgcat($lang, $fn);
204             }
205             };
206             }
207              
208             sub fn_msgfile {
209 0     0 0 0 (my MY $self, my $lang) = @_;
210 0         0 "$self->{cf_dir}/.htyattmsg.$lang.po";
211             }
212              
213             #========================================
214             sub error_handler {
215 11     11 0 35 (my MY $self, my $type, my Error $err) = @_;
216             # どこに出力するか、って問題も有る。 $CON を rewind すべき?
217 11   33     65 my $errcon = try_invoke($self->CON, 'as_error') || do {
218             if ($SYS) {
219             $SYS->make_connection(\*STDOUT, yatt => $self, noheader => 1);
220             } else {
221             \*STDERR;
222             }
223             };
224 11 100       57 if (my $code = $err->{cf_http_status_code}) {
    100          
225 4         14 $errcon->configure(status => $code);
226             } elsif ($code = try_invoke($errcon, [cget => 'status'])) {
227 5         14 $err->{cf_http_status_code} = $code;
228             }
229              
230             # yatt/ytmpl 用の Code generator がまだ無いので、素直に raise.
231             # XXX: 本当は正しくロードできる可能性もあるが,
232             # そこで更に fail すると真のエラーが隠されてしまうため、頑張らない。
233 11 50       64 unless ($self->is_default_cgen_ready) {
234 0         0 die $err;
235             }
236              
237             # error.ytmpl を探し、あれば呼び出す。
238 11         33 my ($sub, $pkg);
239 11 50       59 ($sub, $pkg) = $self->find_renderer($type => ignore_error => 1) or do {
240 11 100 100     50 if ($err->{cf_http_status_code}
241             || $self->{cf_overwrite_status_code_for_errors_as}) {
242             ($sub, $pkg) = (sub {
243 10     10   26 my ($this, $errcon, $err) = @_;
244 10         26 print {*$errcon} $err->reason;
  10         64  
245 10         377 }, $self->EntNS);
246             } else {
247 1         27 die $err;
248             }
249             };
250 10         42 $sub->($pkg, $errcon, $err);
251 10         54 try_invoke($errcon, 'flush_headers');
252             $self->raise_psgi_html($self->{cf_overwrite_status_code_for_errors_as}
253 10   66     61 // $errcon->cget('status')
      50        
254             // 500
255             , $errcon->buffer); # ->DONE was not ok.
256             }
257              
258             Entity dir_config => sub {
259 0     0     my ($this, $name, $default) = @_;
260 0           my MY $self = $this->YATT;
261 0 0         return $self->{cf_dir_config} unless defined $name;
262 0   0       $self->{cf_dir_config}{$name} // $default;
263             };
264              
265 13     13   181 use YATT::Lite::Breakpoint;
  13         35  
  13         800  
266             YATT::Lite::Breakpoint::break_load_dirhandler();
267              
268             1;