File Coverage

blib/lib/HTML/MasonX/Free/Resolver.pm
Criterion Covered Total %
statement 44 46 95.6
branch 18 22 81.8
condition 8 12 66.6
subroutine 8 9 88.8
pod 0 3 0.0
total 78 92 84.7


line stmt bran cond sub pod time code
1             package HTML::MasonX::Free::Resolver 0.007;
2              
3             # ABSTRACT: a resolver that lets you specialize components with dir overlays
4 4     4   200750 use Moose;
  4         1849405  
  4         28  
5              
6             #pod =head1 OVERVIEW
7             #pod
8             #pod This class is a replacement for L<HTML::Mason::Resolver::File>. If you don't
9             #pod know anything about what the resolver does or what comp roots are, this whole
10             #pod thing might make no sense. If you really like L<Mason|HTML::Mason>, though, it
11             #pod might be worth reading about it. Right now.
12             #pod
13             #pod Okay, are you caught up?
14             #pod
15             #pod The next thing you need to keep in mind is that the C<comp_roots> parameter is
16             #pod part of the I<interp> and not part of the I<resolver>. Does this seem weird to
17             #pod you? Me too, but that's how it is.
18             #pod
19             #pod So, let's say you had this set of C<comp_roots>:
20             #pod
21             #pod my_app => /usr/myapp/mason
22             #pod shared => /usr/share/mason
23             #pod
24             #pod The idea is that you can have stuff in the C<my_app> root that specializes
25             #pod generalized stuff in the C<shared> root. Unfortunately, it's not really very
26             #pod useful. You can't have F<foo> in the first comp root inherit from F<foo> in
27             #pod the second. You can't easily take an existing set of templates and specialize
28             #pod them with an overlay.
29             #pod
30             #pod I<That> is the problem that this resolver is meant to solve. Instead of having
31             #pod the resolver try to find each path in each comp root independenly, the
32             #pod C<comp_roots> are instead stored in the resolver's C<resolver_roots>. When
33             #pod looking for a path, it looks in each root in turn. When it finds one, it
34             #pod returns that. If there's another one in one of the later paths, the one that
35             #pod was found will automatically be made to inherit from it and (by default) to
36             #pod call it by default.
37             #pod
38             #pod Because you don't want the interp object to confuse things with comp roots, you
39             #pod must signal that you know that its comp roots will be ignored by setting
40             #pod C<comp_root> to "C</->".
41             #pod
42             #pod Say you set up your resolver roots like this:
43             #pod
44             #pod my_app => /usr/myapp/mason
45             #pod shared => /usr/share/mason
46             #pod
47             #pod Then you have these two files:
48             #pod
49             #pod B<F</usr/share/mason/welcome>>:
50             #pod
51             #pod <h1>Welcome to <& SELF:site &>, <& SELF:user &>!</h1>
52             #pod <%method site>the site</%method>
53             #pod <%method user><% $m->user->name |h %></%method>
54             #pod
55             #pod B<F</usr/myapp/mason>>:
56             #pod
57             #pod <%method site>my guestbook</%method>
58             #pod
59             #pod If you resolve and render F</welcome>, it will say:
60             #pod
61             #pod Welcome to my guestbook, User Name.
62             #pod
63             #pod If you absolutely must render the shared welcome component directly, you can
64             #pod refer to F</shared=/welcome>.
65             #pod
66             #pod This is pretty experimental code. It also probably doesn't work with some
67             #pod Mason options that I don't use, like preloading, because I haven't implemented
68             #pod the C<glob_path> method.
69             #pod
70             #pod =attr comp_class
71             #pod
72             #pod This argument is the class that will be used for components created by this
73             #pod resolver. The default is HTML::Mason::Component::FileBased.
74             #pod
75             #pod Because HTML::MasonX::Resolver::AutoInherit is not (right now) part of
76             #pod Class::Container, you can't pass this as an argument to the interp constructor.
77             #pod
78             #pod =cut
79              
80 4     4   29981 use Carp qw(carp cluck croak);
  4         10  
  4         456  
81 4     4   32 use HTML::Mason::Tools qw(read_file_ref);
  4         9  
  4         239  
82 4     4   3122 use List::AllUtils 'max';
  4         44327  
  4         363  
83              
84 4     4   487 use namespace::autoclean;
  4         7889  
  4         31  
85              
86             sub isa {
87 12     12 0 395319 my ($self, $class) = @_;
88 12 100       185 return 1 if $class eq 'HTML::Mason::Resolver';
89 8         119 return $self->SUPER::isa($class);
90             }
91              
92 0     0 0 0 sub glob_path { croak "unimplemented; $_[0] cannot preload" }
93              
94             # [ [ foo => $path1 ], [ bar => $path2 ] ]
95             # This is really anemic validation.
96             has resolver_roots => (
97             isa => 'ArrayRef',
98             required => 1,
99             traits => [ 'Array' ],
100             handles => { resolver_roots => 'elements' },
101             );
102              
103             has allow_unusual_comp_roots => (
104             is => 'ro',
105             isa => 'Bool',
106             default => 0,
107             );
108              
109             has add_next_call => (
110             is => 'ro',
111             isa => 'Bool',
112             default => 1,
113             );
114              
115             has comp_class => (
116             is => 'ro',
117             isa => 'Str',
118             default => 'HTML::Mason::Component::FileBased',
119             );
120              
121              
122             sub get_info {
123 85     85 0 32214 my ($self, $given_path, $comp_root_key, $comp_root_path) = @_;
124              
125             # It's unfortunate that comp roots are a property of the interpreter and not
126             # the resolver. -- rjbs, 2012-09-19
127 85 50 33     3360 if (
      33        
128             ! $self->allow_unusual_comp_roots
129             and ($comp_root_key ne 'MAIN' or $comp_root_path !~ m{\A[\\/]-\z})
130             ) {
131 0         0 croak "when using HTML::MasonX::Free::Resolver, you must either "
132             . "set the comp_root to '/-' or set allow_unusual_comp_roots to true";
133             }
134              
135 85         180 my ($want_root, $path);
136              
137 85 100       239 if ($given_path =~ /=/) { ($want_root, $path) = split /=/, $given_path;
  49         173  
138 49         200 $want_root =~ s{^/}{}; }
139 36         97 else { ($want_root, $path) = (undef, $given_path) }
140              
141 85         160 my $saw_me;
142             my @seen_in;
143 85         3483 for my $root ($self->resolver_roots) {
144 229         526 my ($root_name, $root_path) = @$root;
145 229 100 100     818 next if $want_root and ! $saw_me and $want_root ne $root_name;
      100        
146 153         228 $saw_me = 1;
147              
148 153         1548 my $fn = File::Spec->canonpath( File::Spec->catfile($root_path, $path) );
149              
150 153 100       3154 push @seen_in, [ $root_name, $fn ] if -e $fn;
151             }
152              
153 85 100       345 return unless @seen_in;
154              
155 65         852 my $modified = (stat $seen_in[0][1])[9];
156              
157 65 50       283 my $base = $comp_root_key eq 'MAIN' ? '' : "/$comp_root_key";
158 65 50       161 $comp_root_key = undef if $comp_root_key eq 'MAIN';
159              
160 65         154 my $srcfile = $seen_in[0][1];
161 65 50       783 return unless -f $srcfile;
162              
163             return HTML::Mason::ComponentSource->new(
164             friendly_name => $srcfile,
165             comp_id => "$base:$seen_in[0][0]=$seen_in[0][1]",
166             last_modified => $modified,
167             comp_path => $given_path,
168             comp_class => $self->comp_class,
169             extra => { comp_root => $comp_root_key },
170             source_callback => sub {
171 32     32   2323 my $body .= ${ read_file_ref($srcfile) };
  32         111  
172 32 100       5309 if (@seen_in > 1) {
173 9 100       405 $body = qq{<%flags>inherit => "/$seen_in[1][0]=$path"</%flags>}
174             . $body . "\n"
175             . ($self->add_next_call ? "% \$m->call_next if \$m->fetch_next;\n"
176             : '');
177             }
178              
179 32         140 \$body;
180             },
181 65         2620 );
182             }
183              
184             1;
185              
186             __END__
187              
188             =pod
189              
190             =encoding UTF-8
191              
192             =head1 NAME
193              
194             HTML::MasonX::Free::Resolver - a resolver that lets you specialize components with dir overlays
195              
196             =head1 VERSION
197              
198             version 0.007
199              
200             =head1 OVERVIEW
201              
202             This class is a replacement for L<HTML::Mason::Resolver::File>. If you don't
203             know anything about what the resolver does or what comp roots are, this whole
204             thing might make no sense. If you really like L<Mason|HTML::Mason>, though, it
205             might be worth reading about it. Right now.
206              
207             Okay, are you caught up?
208              
209             The next thing you need to keep in mind is that the C<comp_roots> parameter is
210             part of the I<interp> and not part of the I<resolver>. Does this seem weird to
211             you? Me too, but that's how it is.
212              
213             So, let's say you had this set of C<comp_roots>:
214              
215             my_app => /usr/myapp/mason
216             shared => /usr/share/mason
217              
218             The idea is that you can have stuff in the C<my_app> root that specializes
219             generalized stuff in the C<shared> root. Unfortunately, it's not really very
220             useful. You can't have F<foo> in the first comp root inherit from F<foo> in
221             the second. You can't easily take an existing set of templates and specialize
222             them with an overlay.
223              
224             I<That> is the problem that this resolver is meant to solve. Instead of having
225             the resolver try to find each path in each comp root independenly, the
226             C<comp_roots> are instead stored in the resolver's C<resolver_roots>. When
227             looking for a path, it looks in each root in turn. When it finds one, it
228             returns that. If there's another one in one of the later paths, the one that
229             was found will automatically be made to inherit from it and (by default) to
230             call it by default.
231              
232             Because you don't want the interp object to confuse things with comp roots, you
233             must signal that you know that its comp roots will be ignored by setting
234             C<comp_root> to "C</->".
235              
236             Say you set up your resolver roots like this:
237              
238             my_app => /usr/myapp/mason
239             shared => /usr/share/mason
240              
241             Then you have these two files:
242              
243             B<F</usr/share/mason/welcome>>:
244              
245             <h1>Welcome to <& SELF:site &>, <& SELF:user &>!</h1>
246             <%method site>the site</%method>
247             <%method user><% $m->user->name |h %></%method>
248              
249             B<F</usr/myapp/mason>>:
250              
251             <%method site>my guestbook</%method>
252              
253             If you resolve and render F</welcome>, it will say:
254              
255             Welcome to my guestbook, User Name.
256              
257             If you absolutely must render the shared welcome component directly, you can
258             refer to F</shared=/welcome>.
259              
260             This is pretty experimental code. It also probably doesn't work with some
261             Mason options that I don't use, like preloading, because I haven't implemented
262             the C<glob_path> method.
263              
264             =head1 PERL VERSION
265              
266             This library should run on perls released even a long time ago. It should work
267             on any version of perl released in the last five years.
268              
269             Although it may work on older versions of perl, no guarantee is made that the
270             minimum required version will not be increased. The version may be increased
271             for any reason, and there is no promise that patches will be accepted to lower
272             the minimum required perl.
273              
274             =head1 ATTRIBUTES
275              
276             =head2 comp_class
277              
278             This argument is the class that will be used for components created by this
279             resolver. The default is HTML::Mason::Component::FileBased.
280              
281             Because HTML::MasonX::Resolver::AutoInherit is not (right now) part of
282             Class::Container, you can't pass this as an argument to the interp constructor.
283              
284             =head1 AUTHOR
285              
286             Ricardo Signes <cpan@semiotic.systems>
287              
288             =head1 COPYRIGHT AND LICENSE
289              
290             This software is copyright (c) 2022 by Ricardo Signes.
291              
292             This is free software; you can redistribute it and/or modify it under
293             the same terms as the Perl 5 programming language system itself.
294              
295             =cut