line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
$Dancer2::Plugin::DoFile::VERSION = '0.12'; |
2
|
|
|
|
|
|
|
# ABSTRACT: File-based MVC plugin for Dancer2 |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
use strict; |
5
|
1
|
|
|
1
|
|
729248
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
22
|
|
6
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
19
|
|
7
|
|
|
|
|
|
|
use Dancer2::Plugin; |
8
|
1
|
|
|
1
|
|
422
|
|
|
1
|
|
|
|
|
9616
|
|
|
1
|
|
|
|
|
7
|
|
9
|
|
|
|
|
|
|
use JSON; |
10
|
1
|
|
|
1
|
|
2918
|
use Hash::Merge; |
|
1
|
|
|
|
|
5909
|
|
|
1
|
|
|
|
|
4
|
|
11
|
1
|
|
|
1
|
|
437
|
|
|
1
|
|
|
|
|
2909
|
|
|
1
|
|
|
|
|
634
|
|
12
|
|
|
|
|
|
|
has page_loc => ( |
13
|
|
|
|
|
|
|
is => 'rw', |
14
|
|
|
|
|
|
|
default => sub {'dofiles/pages'}, |
15
|
|
|
|
|
|
|
); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
has default_file => ( |
18
|
|
|
|
|
|
|
is => 'rw', |
19
|
|
|
|
|
|
|
default => sub {'index'}, |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has extension_list => ( |
23
|
|
|
|
|
|
|
is => 'rw', |
24
|
|
|
|
|
|
|
default => sub { ['.do', '.view']} |
25
|
|
|
|
|
|
|
); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
plugin_keywords 'dofile'; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my %dofiles; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $self = shift; |
32
|
|
|
|
|
|
|
my $settings = $self->config; |
33
|
1
|
|
|
1
|
0
|
57
|
|
34
|
1
|
|
|
|
|
17
|
$settings->{$_} and $self->$_( $settings->{$_} ) |
35
|
|
|
|
|
|
|
for qw/ page_loc default_file extension_list /; |
36
|
|
|
|
|
|
|
} |
37
|
1
|
|
100
|
|
|
58
|
|
38
|
|
|
|
|
|
|
my $plugin = shift; |
39
|
|
|
|
|
|
|
my $arg = shift; |
40
|
|
|
|
|
|
|
my %opts = @_; |
41
|
7
|
|
|
7
|
0
|
133894
|
|
42
|
7
|
|
|
|
|
11
|
my $app = $plugin->app; |
43
|
7
|
|
|
|
|
11
|
my $settings = $app->settings; |
44
|
|
|
|
|
|
|
my $method = $app->request->method; |
45
|
7
|
|
|
|
|
18
|
my $pageroot = $settings->{appdir}; |
46
|
7
|
|
|
|
|
29
|
if ($pageroot !~ /\/$/) { |
47
|
7
|
|
|
|
|
338
|
$pageroot .= "/"; |
48
|
7
|
|
|
|
|
34
|
} |
49
|
7
|
50
|
|
|
|
18
|
$pageroot .= $plugin->page_loc; |
50
|
7
|
|
|
|
|
13
|
|
51
|
|
|
|
|
|
|
my $path = $arg || $app->request->path; |
52
|
7
|
|
|
|
|
15
|
|
53
|
|
|
|
|
|
|
# If any one of these returns content then we stop processing any more of them |
54
|
7
|
|
33
|
|
|
23
|
# Content is defined as an array ref (it's an Obj2HTML array), a hashref with a "content" element, or a scalar (assumed HTML string - it's not checked!) |
55
|
|
|
|
|
|
|
# This can lead to some interesting results if someone doesn't explicitly return undef when they want to fall through to the next file |
56
|
|
|
|
|
|
|
# as perl will return the last evaluated value, which would be intepretted as content according to the above rules |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
my $merger = Hash::Merge->new('RIGHT_PRECEDENT'); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $stash = $opts{stash} || {}; |
61
|
7
|
|
|
|
|
58
|
|
62
|
|
|
|
|
|
|
# Safety first... |
63
|
7
|
|
50
|
|
|
395
|
$path =~ s|/$|"/".$plugin->default_file|e; |
64
|
|
|
|
|
|
|
$path =~ s|^/+||; |
65
|
|
|
|
|
|
|
$path =~ s|\.\./||g; |
66
|
7
|
|
|
|
|
16
|
$path =~ s|~||g; |
|
1
|
|
|
|
|
4
|
|
67
|
7
|
|
|
|
|
22
|
|
68
|
7
|
|
|
|
|
11
|
if (!$path) { $path = $plugin->default_file; } |
69
|
7
|
|
|
|
|
11
|
if (-d $pageroot."/$path") { |
70
|
|
|
|
|
|
|
if ($path =~ /\/$/) { |
71
|
7
|
50
|
|
|
|
19
|
$path .= "/".$plugin->default_file; |
|
0
|
|
|
|
|
0
|
|
72
|
7
|
50
|
|
|
|
176
|
} else { |
73
|
0
|
0
|
|
|
|
0
|
return { |
74
|
0
|
|
|
|
|
0
|
url => "/$path/", |
75
|
|
|
|
|
|
|
redirect => 1, |
76
|
|
|
|
|
|
|
done => 1 |
77
|
0
|
|
|
|
|
0
|
}; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
OUTER: |
82
|
|
|
|
|
|
|
foreach my $ext (@{$plugin->extension_list}) { |
83
|
|
|
|
|
|
|
foreach my $m ("", "-$method") { |
84
|
|
|
|
|
|
|
my $cururl = $path; |
85
|
7
|
|
|
|
|
20
|
my @path = (); |
|
7
|
|
|
|
|
25
|
|
86
|
12
|
|
|
|
|
60
|
|
87
|
18
|
|
|
|
|
115
|
# This iterates back through the path to find the closest FILE downstream, using the rest of the url as a "path" argument |
88
|
18
|
|
|
|
|
22
|
while (!-f $pageroot."/".$cururl.$m.$ext && $cururl =~ s/\/([^\/]*)$//) { |
89
|
|
|
|
|
|
|
if ($1) { unshift(@path, $1); } |
90
|
|
|
|
|
|
|
} |
91
|
18
|
|
100
|
|
|
257
|
|
92
|
5
|
50
|
|
|
|
17
|
# "Do" the file |
|
5
|
|
|
|
|
69
|
|
93
|
|
|
|
|
|
|
if ($cururl) { |
94
|
|
|
|
|
|
|
my $result; |
95
|
|
|
|
|
|
|
if (defined $dofiles{$pageroot."/".$cururl.$m.$ext}) { |
96
|
18
|
50
|
|
|
|
47
|
$result = $dofiles{$pageroot."/".$cururl.$m.$ext}->({path => \@path, this_url => $cururl, dofile_plugin => $plugin, stash => $stash, env => $app->request->env}); |
97
|
18
|
|
|
|
|
21
|
|
98
|
18
|
50
|
|
|
|
160
|
} elsif (-f $pageroot."/".$cururl.$m.$ext) { |
|
|
100
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
our $args = { path => \@path, this_url => $cururl, dofile_plugin => $plugin, stash => $stash, env => $app->request->env }; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
$result = do($pageroot."/".$cururl.$m.$ext); |
102
|
10
|
|
|
|
|
54
|
if ($@ || $!) { $plugin->app->log( error => "Error processing $pageroot / $cururl.$m.$ext: $@ $!\n"); } |
103
|
|
|
|
|
|
|
if (ref $result eq "CODE") { |
104
|
10
|
|
|
|
|
2523
|
$dofiles{$pageroot."/".$cururl.$m.$ext} = $result; |
105
|
10
|
50
|
33
|
|
|
151
|
$result = $result->($args); |
|
0
|
|
|
|
|
0
|
|
106
|
10
|
50
|
|
|
|
23
|
} |
107
|
0
|
|
|
|
|
0
|
} |
108
|
0
|
|
|
|
|
0
|
|
109
|
|
|
|
|
|
|
if (defined $result && ref $result eq "HASH") { |
110
|
|
|
|
|
|
|
if (defined $result->{url} && !defined $result->{done}) { |
111
|
|
|
|
|
|
|
$path = $result->{url}; |
112
|
18
|
100
|
66
|
|
|
77
|
next OUTER; |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
113
|
10
|
100
|
100
|
|
|
27
|
} |
114
|
1
|
|
|
|
|
2
|
if (defined $result->{content} || $result->{url} || $result->{done}) { |
115
|
1
|
|
|
|
|
3
|
return $result; |
116
|
|
|
|
|
|
|
} else { |
117
|
9
|
100
|
100
|
|
|
31
|
$stash = $merger->merge($stash, $result); |
|
|
|
66
|
|
|
|
|
118
|
6
|
|
|
|
|
182
|
} |
119
|
|
|
|
|
|
|
# Move on to the next file |
120
|
3
|
|
|
|
|
10
|
|
121
|
|
|
|
|
|
|
} elsif (ref $result eq "ARRAY") { |
122
|
|
|
|
|
|
|
return { content => $result }; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
} elsif (!ref $result && $result) { |
125
|
0
|
|
|
|
|
0
|
# do we assume this is HTML? Or a file to use in templating? Who knows! |
126
|
|
|
|
|
|
|
return { content => $result }; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
} |
129
|
0
|
|
|
|
|
0
|
} |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# If we got here we didn't find a do file that returned some content |
134
|
|
|
|
|
|
|
return { status => 404 }; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
1
|
|
|
|
|
32
|
my ( $self, $view ) = @_; |
138
|
|
|
|
|
|
|
return path($view); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
my ( $self, $layout ) = @_; |
141
|
0
|
|
|
0
|
0
|
|
return $layout; |
142
|
0
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
1; |
145
|
0
|
|
|
0
|
0
|
|
|
146
|
0
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=pod |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 NAME |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Dancer2::Plugin::DoFile - A file based MVC style plugin for Dancer2 |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 SYNOPSYS |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
In your config.yml |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
plugins: |
158
|
|
|
|
|
|
|
DoFile: |
159
|
|
|
|
|
|
|
page_loc: "dofiles/pages" |
160
|
|
|
|
|
|
|
default_file: "index" |
161
|
|
|
|
|
|
|
extension_list: ['.do','.view'] |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Make sure you have created the directory used for page_loc |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Within a route in dancer2: |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $result = dofile 'path/to/file' |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
You must not include the extension of the file as part of the path, as this will |
170
|
|
|
|
|
|
|
be added per the settings. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Or a default route, with example handling of some return values: |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
prefix '/'; |
175
|
|
|
|
|
|
|
any qr{.*} => sub { |
176
|
|
|
|
|
|
|
my $self = shift; |
177
|
|
|
|
|
|
|
my $result = dofile undef; |
178
|
|
|
|
|
|
|
if ($result && ref $result eq "HASH") { |
179
|
|
|
|
|
|
|
if (defined $result->{status}) { |
180
|
|
|
|
|
|
|
status $result->{status}; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
if (defined $result->{url}) { |
183
|
|
|
|
|
|
|
if (defined $result->{redirect} && $result->{redirect} eq "forward") { |
184
|
|
|
|
|
|
|
return forward $result->{url}; |
185
|
|
|
|
|
|
|
} else { |
186
|
|
|
|
|
|
|
return redirect $result->{url}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} elsif (defined $result->{content}) { |
189
|
|
|
|
|
|
|
return $result->{content}; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
}; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
When the 1st parameter to 'dofile' is undef it'll use the request URI to work |
195
|
|
|
|
|
|
|
out what the file(s) to execute are. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=head1 DESCRIPTION |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
DoFile is a way of automatically pulling multiple perl files to execute as a way |
200
|
|
|
|
|
|
|
to simplify routing complexity in Dancer2 for very large applications. In |
201
|
|
|
|
|
|
|
particular it was designed to offload "as many as possible" URIs that related to |
202
|
|
|
|
|
|
|
some standard functionality through a default route, just by having files |
203
|
|
|
|
|
|
|
existing for the specific URI. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
The magic will look through your filesystem for files to 'do' (execute), and |
206
|
|
|
|
|
|
|
there may be several. The intent is to split out controller files and |
207
|
|
|
|
|
|
|
view files, and these may individually be rolled out or split out. In the |
208
|
|
|
|
|
|
|
default configuration the controller files are suffixed .do, and the view files |
209
|
|
|
|
|
|
|
.view |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head2 File Search Ordering |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
When presented with the URI C<path/to/file> DoFile will begin searching for |
214
|
|
|
|
|
|
|
files that can be executed for this request, until it finds one that returns |
215
|
|
|
|
|
|
|
something that looks like content, a URL or is told you're done, when it stops. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Files are searched: |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=over 4 |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=item * By extension |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
The default extensions .do and .view are checked, unless defined in your |
224
|
|
|
|
|
|
|
config.yml. The intention here is that .do files contain controller code and |
225
|
|
|
|
|
|
|
don't typically return content, but may return redirects. After .do files have |
226
|
|
|
|
|
|
|
been executed, .view files are executed. These are expected to return content. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
You can define as many extensions as you like. You could, for example have: |
229
|
|
|
|
|
|
|
C<['.init','.do','.view','.final']> |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=item * Root/HTTP request method |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
For each extension, first the "root" file C<file.ext> is tested, then a file |
234
|
|
|
|
|
|
|
that matches C<file-METHOD.ext> is tested (where METHOD is the HTTP request |
235
|
|
|
|
|
|
|
method for this request, .ext is the extension). |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=item * Iterating up the directory tree |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
If your call to C<path/to/file> results in a miss for C<path/to/file.do>, DoFile |
240
|
|
|
|
|
|
|
will then test for C<path/to.do> and finally C<path.do> before moving on to |
241
|
|
|
|
|
|
|
C<path/to/file-METHOD.do> |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Once DoFile has found one it will not transcend the directory tree any further. |
244
|
|
|
|
|
|
|
Therefore defining C<path/to/file.do> and C<path/to.do> will not result in |
245
|
|
|
|
|
|
|
both being executed for the URI C<path/to/file> - only the first will be |
246
|
|
|
|
|
|
|
executed. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=back |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
If you define files like so: |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
path.do |
253
|
|
|
|
|
|
|
path/ |
254
|
|
|
|
|
|
|
to.view |
255
|
|
|
|
|
|
|
to/ |
256
|
|
|
|
|
|
|
file-POST.do |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
A POST to the URI C<path/to/file> will execute C<path.do>, then |
259
|
|
|
|
|
|
|
C<path/to/file-POST.do> and finally C<path/to.view>. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=head2 Arguments to the executed files |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
During execution of the file a hashref called $args is available that contains |
264
|
|
|
|
|
|
|
some important things. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
If the executed file returns a coderef, the coderef is executed with this same |
267
|
|
|
|
|
|
|
hashref as the only argument. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=over 4 |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item * path (arrayref) |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Anything that appears after the currently executing file on the URI. For example |
274
|
|
|
|
|
|
|
if I request C</path/to/file> and DoFile is executing C<path-POST.do>, the |
275
|
|
|
|
|
|
|
C<path> element will contain ['to','file'] |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item * this_url (string) |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
The currently executing file without any extension. In the above example this |
280
|
|
|
|
|
|
|
would be C<path>. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=item * stash (hashref) |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
The stash can be initially passed from the router: |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
dofile 'path/to/file', stash => { option => 1 } |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
The stash can be read/written to from each file that executes: |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
if ($args->{stash}->{option} == 1) { |
291
|
|
|
|
|
|
|
$args->{stash}->{anotheroption} = 2; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Or if the file being executed returns a hashref that does not contain any of |
295
|
|
|
|
|
|
|
the elements C<contents>, C<url> or C<done> (see below), it's merged into the |
296
|
|
|
|
|
|
|
stash automatically for passing on to the next file to be executed |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
The stash is used to pass internal state down the file chain. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=item * dofile_plugin (object) |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Just in case the file being executed wants to mess about with Dancer2 or |
303
|
|
|
|
|
|
|
the plugin's internals. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=back |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head2 How DoFile interprets individual executed files response |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
The result (returned value) of each file is checked; if something is returned |
310
|
|
|
|
|
|
|
DoFile will inspect the value to determine what to do next. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head3 Coderef (anonymous sub) |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
You can return a coderef; this will be cached within the plugin and the file |
315
|
|
|
|
|
|
|
will not be checked again, but the coderef will be executed each time that |
316
|
|
|
|
|
|
|
"file" is requested. Generally whether the file should be checked or not is |
317
|
|
|
|
|
|
|
left up to the application (e.g. C<plackup -R ./ -r ...>). |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
In the case a coderef is used, when the code is executed it is passed one |
320
|
|
|
|
|
|
|
argument, a hashref, which is the stash. This saves needing to import the stash |
321
|
|
|
|
|
|
|
from within the code of the file. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
The return of the coderef will be evaluated exactly as below. |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=head3 Internal Redirects |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
If a hashref is returned it's checked for a C<url> element but NO C<done> |
328
|
|
|
|
|
|
|
element. In this case, the DoFile restarts from the begining using the new URL. |
329
|
|
|
|
|
|
|
This is a method for internally redirecting. For example, returning: |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
{ |
332
|
|
|
|
|
|
|
url => "account/login" |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Will cause DoFile to start over with the new URI C<account/login>, without |
336
|
|
|
|
|
|
|
processing any more files from the old URI. The stash is preserved. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head3 Content |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
If a scalar or arrayref is returned, it's wrapped into a hashref into the |
341
|
|
|
|
|
|
|
C<contents> element and sent back to the router. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
If a hashref is returned and contains a C<contents> element, no more files will |
344
|
|
|
|
|
|
|
be processed. The entire hashref is returned to the router. NB: the |
345
|
|
|
|
|
|
|
C<contents> element must contain something that evals to true, else it's |
346
|
|
|
|
|
|
|
considered not there. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head3 Done |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
If a hashref is returned and there is a C<done> element that evals to a true |
351
|
|
|
|
|
|
|
value, DoFile will stop processing files and return the returned hashref to |
352
|
|
|
|
|
|
|
the router. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head3 Continue |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
If a hashref is returned and there is no C<url>, C<content> or C<done> element |
357
|
|
|
|
|
|
|
then the contents of the hasref is combined with the stash and DoFile will look |
358
|
|
|
|
|
|
|
for the next file. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
If nothing is returned at all, DoFile will continue with the next file. |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=head2 What the router gets back |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
DoFile will always return a hashref, even if the files being executed do not |
365
|
|
|
|
|
|
|
return a hashref. This hashref may have anything, but the recommended design |
366
|
|
|
|
|
|
|
is to return one of the following: |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=over 4 |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=item * A C<contents> element |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
The implication is that you've had the web page to be served back. Note that |
373
|
|
|
|
|
|
|
DoFile doesn't care if this is a scalar string or an arrayref. This Plugin |
374
|
|
|
|
|
|
|
was designed to work with Obj2HTML, so in the case of an arrayref the |
375
|
|
|
|
|
|
|
implication is that Obj2HTML should be asked to convert that to HTML. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=item * A C<url> element |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
In this case the router should probably send a 30x response redirecting the |
380
|
|
|
|
|
|
|
client, or perform an internal forward... implementors choice. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item * A C<status> element |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
This could be used to set the status code for returning to the client |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=back |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
DoFile may however return pretty much whatever you want to handle in your final |
389
|
|
|
|
|
|
|
router code. |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=head1 EXAMPLES |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
As noted, what's returned from a DoFile can contain anything. That gives you |
394
|
|
|
|
|
|
|
the opportunity to do pretty much whatever you want with what's returned. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=head1 AUTHOR |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Pero Moretti |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
This software is copyright (c) 2022 by Pero Moretti. |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
406
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |