File Coverage

blib/lib/Mason/Interp.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Mason::Interp;
2             $Mason::Interp::VERSION = '2.22';
3 20     20   122 use Carp;
  20         52  
  20         1314  
4 20     20   18160 use Devel::GlobalDestruction;
  20         63935  
  20         324  
5 20     20   1889 use File::Basename;
  20         58  
  20         1300  
6 20     20   119 use File::Path;
  20         218  
  20         1011  
7 20     20   104 use File::Temp qw(tempdir);
  20         32  
  20         783  
8 20     20   65691 use Guard;
  20         23464  
  20         1256  
9 20     20   11674 use Mason::CodeCache;
  0            
  0            
10             use Mason::Request;
11             use Mason::Result;
12             use Mason::Types;
13             use Mason::Util
14             qw(can_load catdir catfile combine_similar_paths find_wanted first_index is_absolute json_decode mason_canon_path read_file taint_is_on touch_file uniq write_file);
15             use Class::Load;
16             use Memoize;
17             use Moose::Util::TypeConstraints;
18             use Mason::Moose;
19              
20             my $default_out = sub { print( $_[0] ) };
21             my $next_id = 0;
22             my $max_depth = 16;
23              
24             # Passed attributes
25             #
26             has 'allow_globals' => ( isa => 'ArrayRef[Str]', default => sub { [] }, trigger => sub { shift->_validate_allow_globals } );
27             has 'autobase_names' => ( isa => 'ArrayRef[Str]', lazy_build => 1 );
28             has 'autoextend_request_path' => ( isa => 'Bool', default => 1 );
29             has 'class_header' => ( default => '' );
30             has 'comp_root' => ( required => 1, isa => 'Mason::Types::CompRoot', coerce => 1 );
31             has 'component_class_prefix' => ( lazy_build => 1 );
32             has 'data_dir' => ( lazy_build => 1 );
33             has 'dhandler_names' => ( isa => 'ArrayRef[Str]', lazy_build => 1 );
34             has 'index_names' => ( isa => 'ArrayRef[Str]', lazy_build => 1 );
35             has 'mason_root_class' => ( required => 1 );
36             has 'no_source_line_numbers' => ( default => 0 );
37             has 'object_file_extension' => ( default => '.mobj' );
38             has 'plugins' => ( default => sub { [] } );
39             has 'pure_perl_extensions' => ( default => sub { ['.mp'] } );
40             has 'static_source' => ();
41             has 'static_source_touch_file' => ();
42             has 'top_level_extensions' => ( default => sub { ['.mc', '.mp'] } );
43              
44             # Derived attributes
45             #
46             has 'allowed_globals_hash' => ( init_arg => undef, lazy_build => 1 );
47             has 'code_cache' => ( init_arg => undef, lazy_build => 1 );
48             has 'distinct_string_count' => ( init_arg => undef, default => 0 );
49             has 'globals_package' => ( init_arg => undef, lazy_build => 1 );
50             has 'id' => ( init_arg => undef, default => sub { $next_id++ } );
51             has 'match_request_path' => ( init_arg => undef, lazy_build => 1 );
52             has 'pure_perl_regex' => ( lazy_build => 1 );
53             has 'request_params' => ( init_arg => undef );
54             has 'top_level_regex' => ( lazy_build => 1 );
55              
56             # Class overrides
57             #
58             CLASS->_define_class_override_methods();
59              
60             # Allow access to current interp while in load()
61             #
62             our ($current_load_interp);
63             method current_load_interp () { $current_load_interp }
64              
65             #
66             # BUILD
67             #
68              
69             method BUILD ($params) {
70              
71             # Initialize static source mode
72             #
73             if ( $self->{static_source} ) {
74             $self->{static_source_touch_file} ||= catfile( $self->data_dir, 'purge.dat' );
75             $self->{static_source_touch_lastmod} = 0;
76             $self->_check_static_source_touch_file();
77             }
78              
79             # Separate out request parameters
80             #
81             $self->{request_params} = {};
82             my %is_request_attribute =
83             map { ( $_->init_arg || $_->name, 1 ) } $self->request_class->meta->get_all_attributes();
84             foreach my $key ( keys(%$params) ) {
85             if ( $is_request_attribute{$key} ) {
86             $self->{request_params}->{$key} = delete( $params->{$key} );
87             }
88             }
89             }
90              
91             method _build_allowed_globals_hash () {
92             my @allow_globals = uniq( @{ $self->allow_globals } );
93             my @canon_globals = map { join( "", $self->_parse_global_spec($_) ) } @allow_globals;
94             return { map { ( $_, 1 ) } @canon_globals };
95             }
96              
97             method _build_globals_package () {
98             return "Mason::Globals" . $self->id;
99             }
100              
101             method _build_autobase_names () {
102             return [ map { "Base" . $_ } @{ $self->top_level_extensions } ];
103             }
104              
105             method _build_code_cache () {
106             return $self->code_cache_class->new();
107             }
108              
109             method _build_component_class_prefix () {
110             return "MC" . $self->id;
111             }
112              
113             method _build_data_dir () {
114             return tempdir( 'mason-data-XXXX', TMPDIR => 1, CLEANUP => 1 );
115             }
116              
117             method _build_dhandler_names () {
118             return [ map { "dhandler" . $_ } @{ $self->top_level_extensions } ];
119             }
120              
121             method _build_index_names () {
122             return [ map { "index" . $_ } @{ $self->top_level_extensions } ];
123             }
124              
125             method _build_pure_perl_regex () {
126             my $extensions = $self->pure_perl_extensions;
127             if ( !@$extensions ) {
128             return qr/(?!)/; # matches nothing
129             }
130             else {
131             my $regex = join( '|', @$extensions ) . '$';
132             return qr/$regex/;
133             }
134             }
135              
136             method _build_top_level_regex () {
137             my $extensions = $self->top_level_extensions;
138             if ( !@$extensions ) {
139             return qr/./; # matches everything
140             }
141             else {
142             my $regex = join( '|', @$extensions );
143             if ( my @other_names = grep { !/$regex/ } @{ $self->dhandler_names },
144             @{ $self->index_names } )
145             {
146             $regex .= '|(?:/(?:' . join( '|', @other_names ) . '))';
147             }
148             $regex = '(?:' . $regex . ')$';
149             return qr/$regex/;
150             }
151             }
152              
153             #
154             # PUBLIC METHODS
155             #
156              
157             method all_paths ($dir_path) {
158             $dir_path ||= '/';
159             $self->_assert_absolute_path($dir_path);
160             return $self->_collect_paths_for_all_comp_roots(
161             sub {
162             my $root_path = shift;
163             my $dir = $root_path . $dir_path;
164             return ( -d $dir ) ? find_wanted( sub { -f }, $dir ) : ();
165             }
166             );
167             }
168              
169             method comp_exists ($path) {
170              
171             # Canonicalize path
172             #
173             croak "path required" if !defined($path);
174             $path = Mason::Util::mason_canon_path($path);
175              
176             return ( ( $self->static_source && $self->code_cache->get($path) )
177             || $self->_source_file_for_path($path) ) ? 1 : 0;
178             }
179              
180             method flush_code_cache () {
181             my $code_cache = $self->code_cache;
182              
183             foreach my $key ( $code_cache->get_keys() ) {
184             $code_cache->remove($key);
185             }
186             }
187              
188             method glob_paths ($glob_pattern) {
189             return $self->_collect_paths_for_all_comp_roots(
190             sub {
191             my $root_path = shift;
192             return glob( $root_path . $glob_pattern );
193             }
194             );
195             }
196              
197             our $in_load = 0;
198              
199             method load ($path) {
200              
201             local $current_load_interp = $self;
202              
203             my $code_cache = $self->code_cache;
204              
205             # Canonicalize path
206             #
207             croak "path required" if !defined($path);
208             $path = Mason::Util::mason_canon_path($path);
209              
210             # Quick check memory cache in static source mode
211             #
212             if ( $self->static_source ) {
213             if ( my $entry = $code_cache->get($path) ) {
214             return $entry->{compc};
215             }
216             }
217              
218             local $in_load = $in_load + 1;
219             if ( $in_load > $max_depth ) {
220             die ">$max_depth levels deep in inheritance determination (inheritance cycle?)"
221             if $in_load >= $max_depth;
222             }
223              
224             my $compile = 0;
225             my (
226             $default_parent_path, $source_file, $source_lastmod, $object_file,
227             $object_lastmod, @source_stat, @object_stat
228             );
229              
230             my $stat_source_file = sub {
231             if ( $source_file = $self->_source_file_for_path($path) ) {
232             @source_stat = stat $source_file;
233             if ( @source_stat && !-f _ ) {
234             die "source file '$source_file' exists but it is not a file";
235             }
236             }
237             $source_lastmod = @source_stat ? $source_stat[9] : 0;
238             };
239              
240             my $stat_object_file = sub {
241             $object_file = $self->_object_file_for_path($path);
242             @object_stat = stat $object_file;
243             if ( @object_stat && !-f _ ) {
244             die "object file '$object_file' exists but it is not a file";
245             }
246             $object_lastmod = @object_stat ? $object_stat[9] : 0;
247             };
248              
249             # Determine source and object files and their modified times
250             #
251             $stat_source_file->() or return;
252              
253             # Determine default parent comp
254             #
255             $default_parent_path = $self->_default_parent_path($path);
256              
257             if ( $self->static_source ) {
258              
259             if ( $stat_object_file->() ) {
260              
261             # If touch file is more recent than object file, we can't trust object file.
262             #
263             if ( $self->{static_source_touch_lastmod} >= $object_lastmod ) {
264              
265             # If source file is more recent, recompile. Otherwise, touch
266             # the object file so it will be trusted.
267             #
268             if ( $source_lastmod > $object_lastmod ) {
269             $compile = 1;
270             }
271             else {
272             touch_file($object_file);
273             }
274             }
275             }
276             else {
277             $compile = 1;
278             }
279              
280             }
281             else {
282              
283             # Check memory cache
284             #
285             if ( my $entry = $code_cache->get($path) ) {
286             if ( $entry->{source_lastmod} >= $source_lastmod
287             && $entry->{source_file} eq $source_file
288             && $entry->{default_parent_path} eq $default_parent_path )
289             {
290             my $compc = $entry->{compc};
291             if ( $entry->{superclass_signature} eq $self->_superclass_signature($compc) ) {
292             return $compc;
293             }
294             }
295             $code_cache->remove($path);
296             }
297              
298             # Determine object file and its last modified time
299             #
300             $stat_object_file->();
301             $compile = ( !$object_lastmod || $object_lastmod < $source_lastmod );
302             }
303              
304             $self->_compile_to_file( $source_file, $path, $object_file ) if $compile;
305              
306             my $compc = $self->_comp_class_for_path($path);
307              
308             $self->_load_class_from_object_file( $compc, $object_file, $path, $default_parent_path );
309             $compc->meta->make_immutable();
310              
311             # Save component class in the cache.
312             #
313             $code_cache->set(
314             $path,
315             {
316             source_file => $source_file,
317             source_lastmod => $source_lastmod,
318             default_parent_path => $default_parent_path,
319             compc => $compc,
320             superclass_signature => $self->_superclass_signature($compc),
321             }
322             );
323              
324             return $compc;
325             }
326              
327             method _superclass_signature ($compc) {
328             my @superclasses = $compc->meta->superclasses;
329              
330             # Recursively load the superclasses for an existing component class in
331             # case they have changed.
332             #
333             foreach my $superclass (@superclasses) {
334             if ( my $cmeta = $superclass->cmeta ) {
335             my $path = $cmeta->path;
336             $self->load( $cmeta->path );
337             }
338             }
339              
340             # Return a unique signature representing the component class's superclasses
341             # and their versions.
342             #
343             return join( ",", map { join( "-", $_, $_->cmeta ? $_->cmeta->id : 0 ) } @superclasses );
344             }
345              
346             # Memoize comp_exists() and load() - this helps both with components used
347             # multiple times in a request, and with determining default parent
348             # components. The memoize cache is cleared at the beginning of each
349             # request, or in static_source_mode, when the purge file is touched.
350             #
351             memoize('comp_exists');
352             memoize('load');
353              
354             method object_dir () {
355             return catdir( $self->data_dir, 'obj' );
356             }
357              
358             method run () {
359             my %request_params;
360             if ( ref( $_[0] ) eq 'HASH' ) {
361             %request_params = %{ shift(@_) };
362             }
363             my $path = shift;
364             my $request = $self->_make_request(%request_params);
365             $request->run( $path, @_ );
366             }
367              
368             method set_global () {
369             my ( $spec, $value ) = @_;
370             croak "set_global expects a var name and value" unless $value;
371             my ( $sigil, $name ) = $self->_parse_global_spec($spec);
372             croak "${sigil}${name} is not in the allowed globals list"
373             unless $self->allowed_globals_hash->{"${sigil}${name}"};
374              
375             my $varname = sprintf( "%s::%s", $self->globals_package, $name );
376             no strict 'refs';
377             no warnings 'once';
378             $$varname = $value;
379             }
380              
381             #
382             # MODIFIABLE METHODS
383             #
384              
385             method DEMOLISH () {
386             return if in_global_destruction;
387              
388             # We have to check for code_cache slot directly, in case the object gets
389             # destroyed before it has been fully formed (e.g. missing required attr).
390             #
391             $self->flush_code_cache() if defined( $self->{code_cache} );
392             }
393              
394             method _compile ($source_file, $path) {
395             my $compilation = $self->compilation_class->new(
396             source_file => $source_file,
397             path => $path,
398             interp => $self
399             );
400             return $compilation->compile();
401             }
402              
403             method _compile_to_file ($source_file, $path, $object_file) {
404              
405             # We attempt to handle several cases in which a file already exists
406             # and we wish to create a directory, or vice versa. However, not
407             # every case is handled; to be complete, mkpath would have to unlink
408             # any existing file in its way.
409             #
410             if ( defined $object_file && !-f $object_file ) {
411             my ($dirname) = dirname($object_file);
412             if ( !-d $dirname ) {
413             unlink($dirname) if ( -e _ );
414             mkpath( $dirname, 0, 0775 );
415             }
416             rmtree($object_file) if ( -d $object_file );
417             }
418             my $object_contents = $self->_compile( $source_file, $path );
419              
420             $self->write_object_file( $object_file, $object_contents );
421             }
422              
423             method is_pure_perl_comp_path ($path) {
424             return ( $path =~ $self->pure_perl_regex ) ? 1 : 0;
425             }
426              
427             method is_top_level_comp_path ($path) {
428             return ( $path =~ $self->top_level_regex ) ? 1 : 0;
429             }
430              
431             method _load_class_from_object_file ($compc, $object_file, $path, $default_parent_path) {
432             my $flags = $self->_extract_flags_from_object_file($object_file);
433             my $parent_compc =
434             $self->_determine_parent_compc( $path, $flags )
435             || ( $default_parent_path eq '/' && $self->component_class )
436             || $self->load($default_parent_path);
437              
438             my $code = sprintf( 'package %s; use Moose; extends \'%s\'; do(\'%s\'); die $@ if $@',
439             $compc, $parent_compc, $object_file );
440             ($code) = ( $code =~ /^(.*)/s ) if taint_is_on();
441             eval($code);
442             die $@ if $@;
443              
444             $compc->_set_class_cmeta($self);
445             $self->modify_loaded_class($compc);
446             }
447              
448             method modify_loaded_class ($compc) {
449             $self->_add_default_wrap_method($compc);
450             }
451              
452             method write_object_file ($object_file, $object_contents) {
453             write_file( $object_file, $object_contents );
454             }
455              
456             # Given /foo/bar, look for (by default):
457             # /foo/bar/index.{mp,mc},
458             # /foo/bar/dhandler.{mp,mc},
459             # /foo/bar.{mp,mc},
460             # /dhandler.{mp,mc},
461             # /foo.{mp,mc}
462             #
463             method _build_match_request_path ($interp:) {
464              
465             # Create a closure for efficiency - all this data is immutable for an interp.
466             #
467             my @dhandler_subpaths = map { "/$_" } @{ $interp->dhandler_names };
468             my $ignore_file_regex =
469             '(/' . join( "|", @{ $interp->autobase_names }, @{ $interp->dhandler_names } ) . ')$';
470             $ignore_file_regex = qr/$ignore_file_regex/;
471             my @autoextensions = $interp->autoextend_request_path ? @{ $interp->top_level_extensions } : ();
472             my @index_names = @{ $interp->index_names };
473             undef $interp; # So this doesn't end up in closure and cause cycle
474              
475             return sub {
476             my ( $request, $request_path ) = @_;
477             my $interp = $request->interp;
478             my $path_info = '';
479             my $declined_paths = $request->declined_paths;
480             my @index_subpaths = map { "/$_" } @index_names;
481             my $path = $request_path;
482             my @tried_paths;
483              
484             # Deal with trailing slash
485             #
486             $path_info = chop($path) if $path ne '/' && substr( $path, -1 ) eq '/';
487              
488             while (1) {
489             my @candidate_paths =
490             ( $path_info eq '' && !@autoextensions ) ? ($path)
491             : ( $path eq '/' ) ? ( @index_subpaths, @dhandler_subpaths )
492             : (
493             ( grep { !/$ignore_file_regex/ } map { $path . $_ } @autoextensions ),
494             ( map { $path . $_ } ( @index_subpaths, @dhandler_subpaths ) )
495             );
496             push( @tried_paths, @candidate_paths );
497             foreach my $candidate_path (@candidate_paths) {
498             next if $declined_paths->{$candidate_path};
499             if ( my $compc = $interp->load($candidate_path) ) {
500             if (
501             $compc->cmeta->is_top_level
502             && ( $path_info eq ''
503             || $compc->cmeta->is_dhandler
504             || $compc->allow_path_info )
505             )
506             {
507             $request->{path_info} = $path_info;
508             return $compc->cmeta->path;
509             }
510             }
511             }
512             $interp->_top_level_not_found( $request_path, \@tried_paths ) if $path eq '/';
513             my $name = basename($path);
514             $path_info =
515             $path_info eq '/' ? "$name/"
516             : length($path_info) ? "$name/$path_info"
517             : $name;
518             $path = dirname($path);
519             @index_subpaths = (); # only match index file in same directory
520             }
521             };
522             }
523              
524             #
525             # PRIVATE METHODS
526             #
527              
528             method _parse_global_spec () {
529             my $spec = shift;
530             croak "only scalar globals supported at this time (not '$spec')" if $spec =~ /^[@%]/;
531             $spec =~ s/^\$//;
532             die "'$spec' is not a valid global var name" unless $spec =~ qr/^[[:alpha:]_]\w*$/;
533             return ( '$', $spec );
534             }
535              
536             method _add_default_wrap_method ($compc) {
537              
538             # Default wrap method for any component that doesn't define one.
539             # Call inner() until we're back down at the page component ($self),
540             # then call main().
541             #
542             unless ( $compc->meta->has_method('wrap') ) {
543             my $path = $compc->cmeta->path;
544             my $code = sub {
545             my $self = shift;
546             if ( $self->cmeta->path eq $path ) {
547             if ( $self->can('main') ) {
548             $self->main(@_);
549             }
550             else {
551             die sprintf(
552             "component '%s' ('%s') was called but has no main method - did you forget to define 'main' or 'handle'?",
553             $path, $compc->cmeta->source_file );
554             }
555             }
556             else {
557             $compc->_inner();
558             }
559             };
560             $compc->meta->add_augment_method_modifier( wrap => $code );
561             }
562             }
563              
564             method _assert_absolute_path ($path) {
565             $path ||= '';
566             croak "'$path' is not an absolute path" unless is_absolute($path);
567             }
568              
569             method _check_static_source_touch_file () {
570              
571             # Check the static_source_touch_file, if one exists, to see if it has
572             # changed since we last checked. If it has, clear the code cache.
573             #
574             if ( my $touch_file = $self->static_source_touch_file ) {
575             return unless -f $touch_file;
576             my $touch_file_lastmod = ( stat($touch_file) )[9];
577             if ( $touch_file_lastmod > $self->{static_source_touch_lastmod} ) {
578             $self->flush_code_cache;
579             $self->{static_source_touch_lastmod} = $touch_file_lastmod;
580             }
581             }
582             }
583              
584             method _collect_paths_for_all_comp_roots ($code) {
585             my @paths;
586             foreach my $root_path ( @{ $self->comp_root } ) {
587             my $root_path_length = length($root_path);
588             my @files = $code->($root_path);
589             push( @paths, map { substr( $_, $root_path_length ) } @files );
590             }
591             return uniq(@paths);
592             }
593              
594             method _comp_class_for_path ($path) {
595             my $classname = substr( $path, 1 );
596             $classname =~ s/[^\w]/_/g;
597             $classname =~ s/\//::/g;
598             $classname = join( "::", $self->component_class_prefix, $classname );
599             return $classname;
600             }
601              
602             method _construct_distinct_string () {
603             my $number = ++$self->{distinct_string_count};
604             my $str = $self->_construct_distinct_string_for_number($number);
605             return $str;
606             }
607              
608             method _construct_distinct_string_for_number ($number) {
609             my $distinct_delimeter = "__MASON__";
610             return sprintf( "%s%d%s", $distinct_delimeter, $number, $distinct_delimeter );
611             }
612              
613             method _default_parent_path ($orig_path) {
614              
615             # Given /foo/bar.mc, look for (by default):
616             # /foo/Base.mp, /foo/Base.mc,
617             # /Base.mp, /Base.mc
618             #
619             # Split path into dir_path and base_name - validate that it has a
620             # starting slash and ends with at least one non-slash character
621             #
622             my ( $dir_path, $base_name ) = ( $orig_path =~ m{^(/.*?)/?([^/]+)$} )
623             or die "not a valid absolute component path - '$orig_path'";
624             my $path = $dir_path;
625              
626             my @autobase_subpaths = map { "/$_" } @{ $self->autobase_names };
627             while (1) {
628             my @candidate_paths =
629             ( $path eq '/' )
630             ? @autobase_subpaths
631             : ( map { $path . $_ } @autobase_subpaths );
632             if ( ( my $index = first_index { $_ eq $orig_path } @candidate_paths ) != -1 ) {
633             splice( @candidate_paths, 0, $index + 1 );
634             }
635             foreach my $candidate_path (@candidate_paths) {
636             if ( $self->comp_exists($candidate_path) ) {
637             return $candidate_path;
638             }
639             }
640             if ( $path eq '/' ) {
641             return '/';
642             }
643             $path = dirname($path);
644             }
645             }
646              
647             method _determine_parent_compc ($path, $flags) {
648             my $parent_compc;
649             if ( exists( $flags->{extends} ) ) {
650             my $extends = $flags->{extends};
651             if ( defined($extends) ) {
652             $extends = mason_canon_path( join( "/", dirname($path), $extends ) )
653             if substr( $extends, 0, 1 ) ne '/';
654             $parent_compc = $self->load($extends)
655             or die "could not load '$extends' for extends flag";
656             }
657             else {
658             $parent_compc = $self->component_class;
659             }
660             }
661             return $parent_compc;
662             }
663              
664             method _extract_flags_from_object_file ($object_file) {
665             my $flags = {};
666             open( my $fh, "<", $object_file ) or die "could not open '$object_file': $!";
667             my $line = <$fh>;
668             if ( my ($flags_str) = ( $line =~ /\# FLAGS: (.*)/ ) ) {
669             $flags = json_decode($flags_str);
670             }
671             return $flags;
672             }
673              
674             method _flush_load_cache () {
675             Memoize::flush_cache('comp_exists');
676             Memoize::flush_cache('load');
677             }
678              
679             method _make_request () {
680             return $self->request_class->new( interp => $self, %{ $self->request_params }, @_ );
681             }
682              
683             method _object_file_for_path ($path) {
684             return catfile( $self->object_dir, ( split /\//, $path ) ) . $self->object_file_extension;
685             }
686              
687             method _source_file_for_path ($path) {
688             $self->_assert_absolute_path($path);
689             foreach my $root_path ( @{ $self->comp_root } ) {
690             my $source_file = $root_path . $path;
691             return $source_file if -f $source_file;
692             }
693             return undef;
694             }
695              
696             method _top_level_not_found ($path, $tried_paths) {
697             my @combined_paths = combine_similar_paths(@$tried_paths);
698             Mason::Exception::TopLevelNotFound->throw(
699             error => sprintf(
700             "could not resolve request path '%s'; searched for components (%s) under %s\n",
701             $path,
702             join( ", ", map { "'$_'" } @combined_paths ),
703             @{ $self->comp_root } > 1
704             ? "component roots " . join( ", ", map { "'$_'" } @{ $self->comp_root } )
705             : "component root '" . $self->comp_root->[0] . "'"
706             )
707             );
708             }
709              
710             method _validate_allow_globals () {
711              
712             # Will build allowed_globals_hash and also validate the param
713             #
714             $self->allowed_globals_hash;
715             }
716              
717             #
718             # Class overrides. Put here at the bottom because it strangely messes up
719             # Perl line numbering if at the top.
720             #
721             sub _define_class_override_methods {
722             my %class_overrides = (
723             code_cache_class => 'CodeCache',
724             compilation_class => 'Compilation',
725             component_class => 'Component',
726             component_class_meta_class => 'Component::ClassMeta',
727             component_import_class => 'Component::Import',
728             component_moose_class => 'Component::Moose',
729             request_class => 'Request',
730             result_class => 'Result',
731             );
732              
733             # e.g.
734             # $method_name = component_moose_class
735             # $base_method_name = base_component_moose_class
736             # $name = Component::Moose
737             # $default_base_class = Mason::Component::Moose
738             #
739             while ( my ( $method_name, $name ) = each(%class_overrides) ) {
740             my $base_method_name = "base_$method_name";
741             has $method_name => ( init_arg => undef, lazy_build => 1 );
742             has $base_method_name => ( isa => 'Str', lazy_build => 1 );
743             __PACKAGE__->meta->add_method(
744             "_build_$method_name" => sub {
745             my $self = shift;
746             my $base_class = $self->$base_method_name;
747             Class::Load::load_class($base_class);
748             return Mason::PluginManager->apply_plugins_to_class( $base_class, $name,
749             $self->plugins );
750             }
751             );
752             __PACKAGE__->meta->add_method(
753             "_build_$base_method_name" => sub {
754             my $self = shift;
755             my @candidates =
756             map { join( '::', $_, $name ) } ( uniq( $self->mason_root_class, 'Mason' ) );
757             my ($base_class) = grep { can_load($_) } @candidates
758             or die
759             sprintf( "cannot load %s for %s", join( ', ', @candidates ), $base_method_name );
760             return $base_class;
761             }
762             );
763             }
764             }
765              
766             __PACKAGE__->meta->make_immutable();
767              
768             1;
769              
770             __END__
771              
772             =pod
773              
774             =head1 NAME
775              
776             Mason::Interp - Mason Interpreter
777              
778             =head1 SYNOPSIS
779              
780             my $interp = Mason->new(
781             comp_root => '/path/to/comps',
782             data_dir => '/path/to/data',
783             ...
784             );
785             my $output = $interp->run( '/request/path', foo => 5 )->output();
786              
787             =head1 DESCRIPTION
788              
789             Interp is the central Mason object, returned from C<< Mason->new >>. It is
790             responsible for creating new requests, compiling components, and maintaining
791             the cache of loaded components.
792              
793             =head1 PARAMETERS TO THE new() CONSTRUCTOR
794              
795             =over
796              
797             =item allow_globals (varnames)
798              
799             List of one or more global variable names that will be available in all
800             components, like C<< $m >> is by default.
801              
802             allow_globals => [qw($dbh)]
803              
804             As in any programming environment, globals should be created sparingly (if at
805             all) and only when other mechanisms (parameter passing, attributes, singletons)
806             will not suffice. L<Catalyst::View::Mason2|Catalyst::View::Mason2>, for
807             example, creates a C<< $c >> global set to the context object in each request.
808              
809             Set the values of globals with L<set_global|/set_global>.
810              
811             =item autobase_names
812              
813             Array reference of L<autobase|Mason::Manual/Autobase components> filenames to
814             check in order when determining a component's superclass. Default is C<<
815             ["Base.mp", "Base.mc"] >>.
816              
817             =item autoextend_request_path
818              
819             Whether to automatically add the L<top level extensions|/top_level_extensions>
820             (by default ".mp" and ".mc") to the request path when searching for a matching
821             page component. Defaults to true.
822              
823             =item class_header
824              
825             Perl code to be added at the top of the compiled class for every component,
826             e.g. to bring in common features or import common methods. Default is the empty
827             string.
828              
829             # Add to the top of every component class:
830             # use Modern::Perl;
831             # use JSON::XS qw(encode_json decode_json);
832             #
833             my $mason = Mason->new(
834             ...
835             class_header => qq(
836             use Modern::Perl;
837             use JSON::XS qw(encode_json decode_json);
838             ),
839             );
840              
841             This is used by
842             L<Mason::Compilation::output_class_header|Mason::Compilation/output_class_header>.
843             For more advanced usage you can override that method in a subclass or plugin.
844              
845             =item comp_root
846              
847             Required. The component root marks the top of your component hierarchy and
848             defines how component paths are translated into real file paths. For example,
849             if your component root is F</usr/local/httpd/docs>, a component path of
850             F</products/sales.mc> translates to the file
851             F</usr/local/httpd/docs/products/sales.mc>.
852              
853             This parameter may be either a single path or an array reference of paths. If
854             it is an array reference, the paths will be searched in the provided order
855             whenever a component path is resolved, much like Perl's C<< @INC >>.
856              
857             =item component_class_prefix
858              
859             Prefix to use in generated component classnames. Defaults to 'MC' plus the
860             interpreter's count, e.g. MC0. So a component '/foo/bar' would get a classname
861             like 'MC0::foo::bar'.
862              
863             =item data_dir
864              
865             The data directory is a writable directory that Mason uses for various features
866             and optimizations: for example, component object files and data cache files.
867             Mason will create the directory on startup if necessary.
868              
869             Defaults to a temporary directory that will be cleaned up at process end. This
870             will hurt performance as Mason will have to recompile components on each run.
871              
872             =item dhandler_names
873              
874             Array reference of dhandler file names to check in order when resolving a
875             top-level path. Default is C<< ["dhandler.mp", "dhandler.mc"] >>. An empty list
876             disables this feature.
877              
878             =item index_names
879              
880             Array reference of index file names to check in order when resolving a
881             top-level path. Default is C<< ["index.mp", "index.mc"] >>. An empty list
882             disables this feature.
883              
884             =item no_source_line_numbers
885              
886             Do not put in source line number comments when generating code. Setting this
887             to true will cause error line numbers to reflect the real object file, rather
888             than the source component.
889              
890             =item object_file_extension
891              
892             Extension to add to the end of object files. Default is ".mobj".
893              
894             =item plugins
895              
896             A list of plugins and/or plugin bundles:
897              
898             plugins => [
899             'OnePlugin',
900             'AnotherPlugin',
901             '+My::Mason::Plugin::AThirdPlugin',
902             '@APluginBundle',
903             '-DontLikeThisPlugin',
904             ]);
905              
906             See L<Mason::Manual::Plugins>.
907              
908             =item out_method
909              
910             Default L<out_method|Mason::Request/out_method> passed to each new request.
911              
912             =item pure_perl_extensions
913              
914             A listref of file extensions of components to be considered as pure perl (see
915             L<Pure Perl Components|Mason::Manual::Syntax/Pure_Perl_Components>). Default is
916             C<< ['.mp'] >>. If an empty list is specified, then no components will be
917             considered pure perl.
918              
919             =item static_source
920              
921             True or false, default is false. When false, Mason checks the timestamp of the
922             component source file each time the component is used to see if it has changed.
923             This provides the instant feedback for source changes that is expected for
924             development. However it does entail a file stat for each component executed.
925              
926             When true, Mason assumes that the component source tree is unchanging: it will
927             not check component source files to determine if the memory cache or object
928             file has expired. This can save many file stats per request. However, in order
929             to get Mason to recognize a component source change, you must touch the
930             L<static_source_touch_file|/static_source_touch_file>.
931              
932             We recommend turning this mode on in your production sites if possible, if
933             performance is of any concern.
934              
935             =item static_source_touch_file
936              
937             Specifies a filename that Mason will check once at the beginning of every
938             request when in L<static_source|/static_source> mode. When the file timestamp
939             changes (indicating that a component has changed), Mason will clear its
940             in-memory component cache and recheck existing object files.
941              
942             =item top_level_extensions
943              
944             A listref of file extensions of components to be considered "top level",
945             accessible directly from C<< $interp->run >> or a web request. Default is C<<
946             ['.mp', '.mc'] >>. If an empty list is specified, then there will be I<no>
947             restriction; that is, I<all> components will be considered top level.
948              
949             =back
950              
951             =head1 CUSTOM MASON CLASSES
952              
953             These parameters specify alternate classes to use instead of the default
954             Mason:: classes.
955              
956             For example, to use your own Compilation base class:
957              
958             my $interp = Mason->new(base_compilation_class => 'MyApp::Mason::Compilation', ...);
959              
960             L<Relevant plugins|Mason::Manual::Plugins>, if any, will applied to this class
961             to create a final class, which you can get with
962              
963             $interp->compilation_class
964              
965             =over
966              
967             =item base_code_cache_class
968              
969             Specify alternate to L<Mason::CodeCache|Mason::CodeCache>
970              
971             =item base_compilation_class
972              
973             Specify alternate to L<Mason::Compilation|Mason::Compilation>
974              
975             =item base_component_class
976              
977             Specify alternate to L<Mason::Component|Mason::Component>
978              
979             =item base_component_moose_class
980              
981             Specify alternate to L<Mason::Component::Moose|Mason::Component::Moose>
982              
983             =item base_component_class_meta_class
984              
985             Specify alternate to L<Mason::Component::ClassMeta|Mason::Component::ClassMeta>
986              
987             =item base_component_import_class
988              
989             Specify alternate to L<Mason::Component::Import|Mason::Component::Import>
990              
991             =item base_request_class
992              
993             Specify alternate to L<Mason::Request|Mason::Request>
994              
995             =item base_result_class
996              
997             Specify alternate to L<Mason::Result|Mason::Result>
998              
999             =back
1000              
1001             =head1 PUBLIC METHODS
1002              
1003             =over
1004              
1005             =item all_paths ([dir_path])
1006              
1007             Returns a list of distinct component paths under I<dir_path>, which defaults to
1008             '/' if not provided. For example,
1009              
1010             $interp->all_paths('/foo/bar')
1011             => ('/foo/bar/baz.mc', '/foo/bar/blargh.mc')
1012              
1013             Note that these are all component paths, not filenames, and all component roots
1014             are searched if there are multiple ones.
1015              
1016             =item comp_exists (path)
1017              
1018             Returns a boolean indicating whether a component exists for the absolute
1019             component I<path>.
1020              
1021             =item count
1022              
1023             Returns the number of this interpreter, a monotonically increasing integer for
1024             the process starting at 0.
1025              
1026             =item flush_code_cache
1027              
1028             Empties the component cache and removes all component classes.
1029              
1030             =item glob_paths (pattern)
1031              
1032             Returns a list of all component paths matching the glob I<pattern>. e.g.
1033              
1034             $interp->glob_paths('/foo/b*.mc')
1035             => ('/foo/bar.mc', '/foo/baz.mc')
1036              
1037             Note that these are all component paths, not filenames, and all component roots
1038             are searched if there are multiple ones.
1039              
1040             =item load (path)
1041              
1042             Returns the component object corresponding to an absolute component I<path>, or
1043             undef if none exists. Dies with an error if the component fails to load because
1044             of a syntax error.
1045              
1046             =item object_dir
1047              
1048             Returns the directory containing component object files.
1049              
1050             =item run ([request params], path, args...)
1051              
1052             Creates a new L<Mason::Request|Mason::Request> object for the given I<path> and
1053             I<args>, and executes it. Returns a L<Mason::Result|Mason::Result> object,
1054             which is generally accessed to get the output. e.g.
1055              
1056             my $output = $interp->run('/foo/bar', baz => 5)->output;
1057              
1058             The first argument may optionally be a hashref of request parameters, which are
1059             passed to the Mason::Request constructor. e.g. this tells the request to output
1060             to standard output:
1061              
1062             $interp->run({out_method => sub { print $_[0] }}, '/foo/bar', baz => 5);
1063              
1064             =item set_global (varname, value)
1065              
1066             Set the global I<varname> to I<value>. This will be visible in all components
1067             loaded by this interpreter. The variables must be on the
1068             L<allow_globals|/allow_globals> list.
1069              
1070             $interp->set_global('$scalar', 5);
1071             $interp->set_global('$scalar2', $some_object);
1072              
1073             See also L<set_global|Mason::Request/set_global>.
1074              
1075             =back
1076              
1077             =head1 MODIFIABLE METHODS
1078              
1079             These methods are not intended to be called externally, but may be useful to
1080             modify with method modifiers in L<plugins|Mason::Manual::Plugins> and
1081             L<subclasses|Mason::Manual::Subclasses>. Their APIs will be kept as stable as
1082             possible.
1083              
1084             =over
1085              
1086             =item is_pure_perl_comp_path ($path)
1087              
1088             Determines whether I<$path> is a pure Perl component - by default, uses
1089             L<pure_perl_extensions|/pure_perl_extensions>.
1090              
1091             =item is_top_level_comp_path ($path)
1092              
1093             Determines whether I<$path> is a valid top-level component - by default, uses
1094             L<top_level_extensions|/top_level_extensions>.
1095              
1096             =item modify_loaded_class ( $compc )
1097              
1098             An opportunity to modify loaded component class I<$compc> (e.g. add additional
1099             methods or apply roles) before it is made immutable.
1100              
1101             =item write_object_file ($object_file, $object_contents)
1102              
1103             Write compiled component I<$object_contents> to I<$object_file>. This is an
1104             opportunity to modify I<$object_contents> before it is written, or
1105             I<$object_file> after it is written.
1106              
1107             =back
1108              
1109             =head1 SEE ALSO
1110              
1111             L<Mason|Mason>
1112              
1113             =head1 AUTHOR
1114              
1115             Jonathan Swartz <swartz@pobox.com>
1116              
1117             =head1 COPYRIGHT AND LICENSE
1118              
1119             This software is copyright (c) 2012 by Jonathan Swartz.
1120              
1121             This is free software; you can redistribute it and/or modify it under
1122             the same terms as the Perl 5 programming language system itself.
1123              
1124             =cut