File Coverage

blib/lib/YATT/Lite/WebMVC0/DirApp.pm
Criterion Covered Total %
statement 89 129 68.9
branch 31 66 46.9
condition 18 40 45.0
subroutine 19 26 73.0
pod 1 10 10.0
total 158 271 58.3


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