File Coverage

blib/lib/YATT/Lite.pm
Criterion Covered Total %
statement 181 243 74.4
branch 43 82 52.4
condition 14 40 35.0
subroutine 45 57 78.9
pod 5 36 13.8
total 288 458 62.8


line stmt bran cond sub pod time code
1             package YATT::Lite; sub MY () {__PACKAGE__}
2 13     13   10757 use strict;
  13         27  
  13         424  
3 13     13   67 use warnings qw(FATAL all NONFATAL misc);
  13         25  
  13         532  
4 13     13   287 use 5.010; no if $] >= 5.017011, warnings => "experimental";
  13     13   42  
  13         334  
  13         6236  
  13         114  
5              
6 13     13   836 use Carp qw(carp croak confess);
  13         29  
  13         996  
7             our $VERSION = '0.100_002';
8             #use mro 'c3';
9              
10 13     13   68 use Scalar::Util qw/weaken/;
  13         25  
  13         868  
11              
12             #
13             # YATT Internalへの Facade. YATT の初期化パラメータの保持者でもある。
14             #
15 13     13   220 use parent qw/YATT::Lite::Object File::Spec/;
  13         3816  
  13         82  
16 13         107 use YATT::Lite::MFields qw/YATT
17             cf_dir
18             cf_vfs cf_base
19             cf_factory
20             cf_header_charset
21             cf_output_encoding
22             cf_tmpl_encoding
23             cf_index_name
24             cf_ext_public
25             cf_ext_private
26             cf_app_ns entns
27             cf_app_name
28             cf_debug_cgen cf_debug_parser cf_namespace cf_only_parse
29             cf_special_entities cf_no_lineinfo cf_check_lineno
30             cf_rc_script
31             cf_tmpl_cache
32             cf_dont_map_args
33             cf_dont_debug_param
34             cf_info
35             cf_lcmsg_sink
36             cf_always_refresh_deps
37              
38             cf_default_lang
39              
40             cf_path2entns
41 13     13   4270 /;
  13         27  
42              
43             MY->cf_mkaccessors(qw/app_name/);
44              
45             # Entities を多重継承する理由は import も継承したいから。
46             # XXX: やっぱり、 YATT::Lite には固有の import を用意すべきではないか?
47             # yatt_default や cgen_perl を定義するための。
48 13     13   6382 use YATT::Lite::Entities -as_base, qw(*YATT *CON *SYS);
  13         29  
  13         197  
49              
50             # For error, raise, DONE. This is inserted to ISA too.
51 13     13   6115 use YATT::Lite::Partial::ErrorReporter;
  13         28  
  13         112  
52              
53 13     13   6015 use YATT::Lite::Partial::AppPath;
  13         36  
  13         76  
54              
55 13         25507 use YATT::Lite::Util qw/globref lexpand extname ckrequire terse_dump escape
56             set_inc ostream try_invoke list_isa symtab
57             look_for_globref
58             subname ckeval
59             secure_text_plain
60             define_const
61 13     13   76 /;
  13         27  
62              
63             sub Facade () {__PACKAGE__}
64 4     4 0 27 sub default_app_ns {'MyApp'}
65 36     36 0 75 sub default_trans {'YATT::Lite::Core'}
66 3     3 0 28 sub default_export {(shift->SUPER::default_export, qw(Entity *SYS *CON))}
67 0     0 0 0 sub default_index_name { '' }
68 21     21 0 67 sub default_ext_public {'yatt'}
69 21     21 0 64 sub default_ext_private {'ytmpl'}
70              
71             sub with_system {
72 45     45 0 146 (my MY $self, local $SYS, my $method) = splice @_, 0, 3;
73 45         253 $self->$method(@_);
74             }
75              
76             sub after_new {
77 53     53 1 94 (my MY $self) = @_;
78 53         240 $self->SUPER::after_new;
79 53   100     227 $self->{cf_index_name} //= "";
80 53   66     222 $self->{cf_ext_public} //= $self->default_ext_public;
81 53   66     201 $self->{cf_ext_private} //= $self->default_ext_private;
82 53         236 weaken($self->{cf_factory});
83             }
84              
85             # XXX: kludge!
86             sub create_neighbor {
87 10     10 0 19 (my MY $self, my ($dir)) = @_;
88 10         41 my MY $yatt = $self->{cf_factory}->load_yatt($dir);
89 10         67 $yatt->get_trans->root;
90             }
91              
92             #========================================
93             # file extension based handler dispatching.
94             #========================================
95              
96             sub handle {
97 45     45 0 129 (my MY $self, my ($ext, $con, $file)) = @_;
98 45         110 local ($YATT, $CON) = ($self, $con);
99 45         192 $con->configure(yatt => $self);
100 45 50       159 if (my $enc = $self->{cf_output_encoding}) {
101 45         134 $con->configure(encoding => $enc);
102             }
103              
104 45 50       133 unless (defined $file) {
105 0         0 confess "\n\nFilename for DirHandler->handle() is undef!"
106             ." in $self->{cf_app_ns}.\n";
107             }
108              
109 45         209 my $sub = $YATT->find_handler($ext, $file, $CON);
110 45         146 $sub->($YATT, $CON, $file);
111              
112 41         7408 try_invoke($CON, 'flush_headers');
113              
114 41         576 $CON;
115             }
116              
117             sub render {
118 4     4 1 85 my MY $self = shift;
119 4         6 my $buffer; {
120 4 50       7 my $con = $SYS
  4         26  
121             ? $SYS->make_connection(undef, buffer => \$buffer, yatt => $self)
122             : ostream(\$buffer);
123 4         67 $self->render_into($con, @_);
124             }
125 4         32 $buffer;
126             }
127              
128             sub render_into {
129 7     7 1 37 local ($YATT, $CON) = splice @_, 0, 2;
130 7         38 $YATT->open_trans->render_into($CON, @_);
131 7         33 try_invoke($CON, 'flush_headers');
132             }
133              
134             sub find_handler {
135 33     33 1 90 (my MY $self, my ($ext, $file, $con)) = @_;
136 33   0     96 $ext //= $self->cut_ext($file) || $self->{cf_ext_public};
      33        
137 33 100       124 $ext = "yatt" if $ext eq $self->{cf_ext_public};
138 33 50       214 my $sub = $self->can("_handle_$ext")
139             or die "Unsupported file type: $ext";
140 33         97 $sub;
141             }
142              
143             #----------------------------------------
144              
145             # 直接呼ぶことは禁止。∵ $YATT, $CON を設定するのは handle の役目だから。
146             sub _handle_yatt {
147 27     27   59 (my MY $self, my ($con, $file)) = @_;
148              
149 27         121 my ($part, $sub, $pkg, $args)
150             = $self->prepare_part_handler($con, $file);
151              
152 26         103 $sub->($pkg, $con, @$args);
153              
154 25         179 $con;
155             }
156              
157             sub _handle_ytmpl {
158 0     0   0 (my MY $self, my ($con, $file)) = @_;
159             # XXX: http result code:
160 0         0 print $con "Forbidden filetype: $file";
161             }
162              
163             #----------------------------------------
164              
165             sub prepare_part_handler {
166 0     0 0 0 (my MY $self, my ($con, $file)) = @_;
167              
168 0         0 my $trans = $self->open_trans;
169              
170 0         0 my $mapped = [$file, my ($type, $item) = $self->parse_request_sigil($con)];
171 0 0 0     0 if (not $self->{cf_dont_debug_param}
172             and -e ".htdebug_param") {
173 0         0 $self->dump($mapped, [map {[$_ => $con->param($_)]} $con->param]);
  0         0  
174             }
175              
176             # XXX: public に限定するのはどこで? ここで?それとも find_自体?
177 0         0 my ($part, $sub, $pkg) = $trans->find_part_handler($mapped);
178 0 0       0 unless ($part->public) {
179             # XXX: refresh する手もあるだろう。
180 0         0 croak $self->error(q|Forbidden request %s|, terse_dump($mapped));
181             }
182              
183 0         0 my @args; @args = $part->reorder_cgi_params($con)
184 0 0 0     0 unless $self->{cf_dont_map_args} || $part->isa($trans->Action);
185              
186 0         0 ($part, $sub, $pkg, \@args);
187             }
188              
189             sub parse_request_sigil {
190 27     27 0 55 (my MY $self, my ($con)) = @_;
191 27         39 my ($subpage, $action);
192             # XXX: url_param
193 27         123 foreach my $name (grep {defined} $con->param()) {
  6         67  
194 6 100       43 my ($sigil, $word) = $name =~ /^([~!])(\1|\w*)$/
195             or next;
196             # If $name in ('~~', '!!'), use value.
197 3 50       16 my $new = $word eq $sigil ? $con->param($name) : $word;
198             # else use $word from ~$word.
199             # Note: $word may eq ''. This is for render_/action_.
200 3         28 given ($sigil) {
201 3         10 when ('~') {
202 1 50       4 if (defined $subpage) {
203 0         0 $self->error("Duplicate subpage request! %s vs %s"
204             , $subpage, $new);
205             }
206 1         4 $subpage = $new;
207             }
208 2         5 when ('!') {
209 2 50       7 if (defined $action) {
210 0         0 $self->error("Duplicate action! %s vs %s"
211             , $action, $new);
212             }
213 2         7 $action = $new;
214             }
215 0         0 default {
216 0         0 croak "Really?";
217             }
218             }
219             }
220 27 50 66     186 if (defined $subpage and defined $action) {
    100          
    100          
221             # XXX: Reserved for future use.
222 0         0 $self->error("Can't use subpage and action at one time: %s vs %s"
223             , $subpage, $action);
224             } elsif (defined $subpage) {
225 1         5 (page => $subpage);
226             } elsif (defined $action) {
227 2         9 (action => $action);
228             } else {
229 24         86 ();
230             }
231             }
232              
233             sub cut_ext {
234 45     45 0 97 my ($self, $fn) = @_;
235 45 50       148 croak "Undefined filename!" unless defined $fn;
236 45 50       299 return undef unless $fn =~ s/\.(\w+$)//;
237 45         331 $1;
238             }
239              
240             #========================================
241             # hook
242             #========================================
243       48 0   sub finalize_connection {}
244              
245             #========================================
246             # Output encoding. Used in scripts/yatt*
247             #========================================
248             sub fconfigure_encoding {
249 0     0 0 0 my MY $self = shift;
250 0 0       0 return unless $self->{cf_output_encoding};
251 0         0 my $enc = "encoding($self->{cf_output_encoding})";
252 0         0 require PerlIO;
253 0         0 foreach my $fh (@_) {
254 0 0       0 next if grep {$_ eq $enc} PerlIO::get_layers($fh);
  0         0  
255 0         0 binmode($fh, ":$enc");
256             }
257 0         0 $self;
258             }
259              
260             #========================================
261             # Delayed loading of YATT::Lite::Core
262             #========================================
263              
264             *open_vfs = *open_trans; *open_vfs = *open_trans;
265             sub open_trans {
266 34     34 0 60 (my MY $self) = @_;
267 34         131 my $trans = $self->get_trans;
268 34         204 $trans->reset_refresh_mark;
269 34         154 $trans;
270             }
271              
272             *get_vfs = *get_trans; *get_vfs = *get_trans;
273             sub get_trans {
274 415     415 0 642 (my MY $self) = @_;
275 415 100       2837 $self->{YATT} || $self->build_trans($self->{cf_tmpl_cache});
276             }
277              
278             sub build_trans {
279 36     36 0 116 (my MY $self, my ($vfscache, $vfsspec, @rest)) = @_;
280 36         202 my $class = $self->default_trans;
281 36         149 ckrequire($class);
282              
283 36 50       68 my @vfsspec = @{$vfsspec || $self->{cf_vfs}};
  36         237  
284 36 50       124 push @vfsspec, base => $self->{cf_base} if $self->{cf_base};
285              
286             $self->{YATT} = $class->new
287             (\@vfsspec
288             , facade => $self
289             , cache => $vfscache
290             , entns => $self->{entns}
291             , @rest
292             # XXX: Should be more extensible.
293 36         349 , $self->cf_delegate_defined(qw/namespace base
294             die_in_error tmpl_encoding
295             debug_cgen debug_parser
296             special_entities no_lineinfo check_lineno
297             index_name
298             ext_public
299             ext_private
300             rc_script
301             lcmsg_sink
302             only_parse
303             always_refresh_deps
304             /));
305             }
306              
307             sub _before_after_new {
308 53     53   111 (my MY $self) = @_;
309 53   33     171 $self->{cf_app_ns} //= $self->default_app_ns;
310 53         279 $self->{entns} = $self->ensure_entns($self->{cf_app_ns});
311             }
312              
313             #========================================
314             # Entity
315             #========================================
316              
317 30     30 0 114 sub root_EntNS { 'YATT::Lite::Entities' }
318              
319             # ${app_ns}::EntNS を作り、(YATT::Lite::Entities へ至る)継承関係を設定する。
320             # $app_ns に EntNS constant を追加する。
321             # 複数回呼ばれた場合、既に定義済みの entns を返す
322              
323             sub ensure_entns {
324 239     239 0 1632 my ($mypack, $app_ns, @baseclass) = @_;
325 239         616 my $entns = "${app_ns}::EntNS";
326              
327 13     13   78 my $sym = do {no strict 'refs'; \*{$entns}};
  13         26  
  13         9133  
  239         297  
  239         277  
  239         996  
328 239 100       358 if (*{$sym}{CODE}) {
  239         730  
329             # croak "EntNS for $app_ns is already defined!";
330 110         383 return $entns;
331             }
332              
333             # mro::set_mro($entns, 'c3'); # XXX: Should change to c3, but...
334              
335             # $app_ns が %FIELDS 定義を持たない時(ex YLObjectでもPartialでもない)に限り、
336             # YATT::Lite への継承を設定する
337 129 100       600 unless (YATT::Lite::MFields->has_fields($app_ns)) {
338             # XXX: $mypack への継承にすると、あちこち動かなくなるぜ?なんで?
339 21         110 YATT::Lite::MFields->add_isa_to($app_ns, MY)->define_fields($app_ns);
340             }
341              
342 129 50       313 unless (grep {$_->can("EntNS")} @baseclass) {
  85         1104  
343 129   66     436 my $base = try_invoke($app_ns, 'EntNS') // $mypack->root_EntNS;
344             # print "insert base '$base' for entns $entns\n";
345 129         372 unshift @baseclass, $base;
346             }
347              
348             # print "entns $entns should inherits: @baseclass\n";
349 129         474 YATT::Lite::MFields->add_isa_to($entns, @baseclass);
350              
351 129         404 set_inc($entns, 1);
352              
353             # EntNS() を足すのは最後にしないと、再帰継承に陥る
354 129 50       172 unless (my $code = *{$sym}{CODE}) {
  129 0       396  
355 129         408 define_const($sym, $entns);
356             } elsif ((my $old = $code->()) ne $entns) {
357 0         0 croak "Can't add EntNS() to '$app_ns'. Already has EntNS as $old!";
358             } else {
359             # ok.
360             }
361 129         474 $entns
362             }
363              
364             sub list_entns {
365 23     23 0 54 my ($pack, $inspected) = @_;
366             map {
367 23 100       160 defined(symtab($_)->{'EntNS'}) ? join("::", $_, 'EntNS') : ()
  59         307  
368             } list_isa($inspected)
369             }
370              
371             # use YATT::Lite qw(Entity); で呼ばれ、
372             # $callpack に Entity 登録関数を加える.
373             sub define_Entity {
374 79     79 0 211 my ($myPack, $opts, $callpack, @base) = @_;
375              
376             # Entity を追加する先は、 $callpack が Object 系か、 stateless 系かで変化する
377             # Object 系の場合は、 ::EntNS を作ってそちらに加え, 同時に YATT() も定義する
378 79         223 my $is_objclass = is_objclass($callpack);
379 79 100       373 my $destns = $is_objclass
380             ? $myPack->ensure_entns($callpack, @base)
381             : $callpack;
382              
383             # 既にあるなら何もしない。... バグの温床にならないことを祈る。
384 79         249 my $ent = globref($callpack, 'Entity');
385 79 100       135 unless (*{$ent}{CODE}) {
  79         262  
386             *$ent = sub {
387 174     174   322 my ($name, $sub) = @_;
388 174         442 my $longname = join "::", $destns, "entity_$name";
389 174         466 subname($longname, $sub);
390 174 50       470 print "defining entity_$name in $destns\n" if $ENV{DEBUG_ENTNS};
391 174         204 *{globref($destns, "entity_$name")} = $sub;
  174         579  
392 78         432 };
393             }
394              
395 79 100       248 if ($is_objclass) {
396 76         119 *{globref($destns, 'YATT')} = *YATT;
  76         246  
397              
398 76 100       1082 unless ($callpack->can("entity")) {
399 16         117 *{globref($callpack, "entity")} = $myPack->can('entity');
  16         59  
400             }
401             }
402              
403 79         1953 return $destns;
404             }
405              
406             # ここで言う Object系とは、
407             # YATT::Lite::Object を継承してるか、
408             # 又は既に %FIELDS が定義されている class
409             # のこと
410             sub is_objclass {
411 79     79 0 134 my ($class) = @_;
412 79 100       453 return 1 if UNIVERSAL::isa($class, 'YATT::Lite::Object');
413 12 100       49 my $sym = look_for_globref($class, 'FIELDS')
414             or return 0;
415 9         18 *{$sym}{HASH};
  9         31  
416             }
417              
418             sub entity {
419 0     0 1 0 (my MY $yatt, my $name) = splice @_, 0, 2;
420 0         0 my $this = $yatt->EntNS;
421 0         0 $this->can("entity_$name")->($this, @_);
422             }
423              
424             BEGIN {
425 13     13   70 MY->define_Entity(undef, MY);
426             }
427              
428             #========================================
429             # Locale gettext support.
430             #========================================
431              
432             sub use_encoded_config {
433 0     0 0 0 (my MY $self) = @_;
434             $self->{cf_tmpl_encoding}
435 0         0 }
436              
437 13     13   9022 use YATT::Lite::Partial::Gettext;
  13         33  
  13         124  
438              
439             # Extract (and cache, for later merging) l10n msgs from filelist.
440             # By default, it merges $filelist into existing locale_cache.
441             # To get fresh list, explicitly pass $msglist=[].
442             #
443             sub lang_extract_lcmsg {
444 2     2 0 7 (my MY $self, my ($lang, $filelist, $msglist, $msgdict)) = @_;
445              
446 2 0 33     8 if (not $msglist and not $msgdict) {
447 0         0 ($msglist, $msgdict) = $self->lang_msgcat($lang)
448             }
449              
450 2         7 $self->get_trans->extract_lcmsg($filelist, $msglist, $msgdict);
451             }
452              
453 0     0 0 0 sub default_default_lang { 'en' }
454             sub default_lang {
455 0     0 0 0 (my MY $self) = @_;
456 0 0       0 $self->{cf_default_lang} || $self->default_default_lang;
457             }
458              
459             #========================================
460             # Delegation to the core(Translator, which is useless for non-templating.)
461             #========================================
462             foreach
463             (qw/find_part
464             find_file
465             find_product
466             find_renderer
467             find_part_handler
468             ensure_parsed
469              
470             list_items
471              
472             add_to
473             /
474             ) {
475             my $meth = $_;
476             *{globref(MY, $meth)} = subname(join("::", MY, $meth)
477 366     366   18387 , sub { shift->get_trans->$meth(@_) });
478             }
479              
480             sub dump {
481 0     0 0 0 my MY $self = shift;
482             # XXX: charset...
483             die [200, [$self->secure_text_plain]
484 0         0 , [map {terse_dump($_)."\n"} @_]];
  0         0  
485             }
486              
487             #========================================
488             # Builtin Entities.
489             #========================================
490              
491             sub YATT::Lite::EntNS::entity_template {
492 3     3 0 76 my ($this, $pkg) = @_;
493 3   33     13 $YATT->get_trans->find_template_from_package($pkg // $this);
494             };
495              
496             sub YATT::Lite::EntNS::entity_stash {
497 0     0 0   my $this = shift;
498 0           my $prop = $CON->prop;
499 0   0       my $stash = $prop->{stash} //= {};
500 0 0         unless (@_) {
    0          
    0          
    0          
501 0           $stash
502             } elsif (@_ > 1) {
503 0           %$stash = @_;
504             } elsif (not defined $_[0]) {
505 0           carp "Undefined argument for :stash()";
506             } elsif (ref $_[0]) {
507 0           $prop->{stash} = $_[0]
508             } else {
509 0           $stash->{$_[0]};
510             }
511             };
512              
513             sub YATT::Lite::EntNS::entity_mkhidden {
514 0     0 0   my ($this) = shift;
515             \ join "\n", map {
516 0           my $name = $_;
  0            
517 0           my $esc = escape($name);
518             map {
519 0           sprintf(qq||
  0            
520             , $esc, escape($_));
521             } $CON->param($name);
522             } @_;
523             };
524              
525             sub YATT::Lite::EntNS::entity_file_rootname {
526 0     0 0   my ($this, $fn) = @_;
527 0   0       $fn //= $CON->file();
528 0           $fn =~ s/\.\w+$//;
529 0           $fn;
530             };
531              
532             #----------------------------------------
533 13     13   91 use YATT::Lite::Breakpoint ();
  13         25  
  13         620  
534             YATT::Lite::Breakpoint::break_load_facade();
535              
536             1;