source: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes/Route.pm@ 32205

Last change on this file since 32205 was 32205, checked in by ak19, 6 years ago

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

File size: 18.9 KB
Line 
1package Mojolicious::Routes::Route;
2use Mojo::Base -base;
3
4use Carp ();
5use Mojo::Util;
6use Mojolicious::Routes::Pattern;
7use Scalar::Util ();
8
9has [qw(inline parent partial)];
10has 'children' => sub { [] };
11has pattern => sub { Mojolicious::Routes::Pattern->new };
12
13sub AUTOLOAD {
14 my $self = shift;
15
16 my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
17 Carp::croak "Undefined subroutine &${package}::$method called"
18 unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
19
20 # Call shortcut with current route
21 Carp::croak qq{Can't locate object method "$method" via package "$package"}
22 unless my $shortcut = $self->root->shortcuts->{$method};
23 return $self->$shortcut(@_);
24}
25
26sub add_child {
27 my ($self, $route) = @_;
28 Scalar::Util::weaken $route->remove->parent($self)->{parent};
29 push @{$self->children}, $route;
30 $route->pattern->types($self->root->types);
31 return $self;
32}
33
34sub any { shift->_generate_route(ref $_[0] eq 'ARRAY' ? shift : [], @_) }
35
36sub delete { shift->_generate_route(DELETE => @_) }
37
38sub detour { shift->partial(1)->to(@_) }
39
40sub find { shift->_index->{shift()} }
41
42sub get { shift->_generate_route(GET => @_) }
43
44sub has_custom_name { !!shift->{custom} }
45
46sub has_websocket {
47 my $self = shift;
48 return $self->{has_websocket} if exists $self->{has_websocket};
49 return $self->{has_websocket} = grep { $_->is_websocket } @{$self->_chain};
50}
51
52sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }
53
54sub is_websocket { !!shift->{websocket} }
55
56sub name {
57 my $self = shift;
58 return $self->{name} unless @_;
59 @$self{qw(name custom)} = (shift, 1);
60 return $self;
61}
62
63sub options { shift->_generate_route(OPTIONS => @_) }
64
65sub over {
66 my $self = shift;
67
68 # Routes with conditions can't be cached
69 return $self->{over} unless @_;
70 my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
71 return $self unless @$conditions;
72 $self->{over} = $conditions;
73 $self->root->cache->max_keys(0);
74
75 return $self;
76}
77
78sub parse {
79 my $self = shift;
80 $self->{name} = $self->pattern->parse(@_)->unparsed // '';
81 $self->{name} =~ s/\W+//g;
82 return $self;
83}
84
85sub patch { shift->_generate_route(PATCH => @_) }
86sub post { shift->_generate_route(POST => @_) }
87sub put { shift->_generate_route(PUT => @_) }
88
89sub remove {
90 my $self = shift;
91 return $self unless my $parent = $self->parent;
92 @{$parent->children} = grep { $_ ne $self } @{$parent->children};
93 return $self->parent(undef);
94}
95
96sub render {
97 my ($self, $values) = @_;
98 my $path = join '',
99 map { $_->pattern->render($values, !@{$_->children} && !$_->partial) }
100 @{$self->_chain};
101 return $path || '/';
102}
103
104sub root { shift->_chain->[0] }
105
106sub route {
107 my $self = shift;
108 my $route = $self->add_child(__PACKAGE__->new->parse(@_))->children->[-1];
109 my $format = $self->pattern->constraints->{format};
110 $route->pattern->constraints->{format} //= 0 if defined $format && !$format;
111 return $route;
112}
113
114sub suggested_method {
115 my $self = shift;
116
117 my %via;
118 for my $route (@{$self->_chain}) {
119 next unless my @via = @{$route->via || []};
120 %via = map { $_ => 1 } keys %via ? grep { $via{$_} } @via : @via;
121 }
122
123 return 'POST' if $via{POST} && !$via{GET};
124 return $via{GET} ? 'GET' : (sort keys %via)[0] || 'GET';
125}
126
127sub to {
128 my $self = shift;
129
130 my $pattern = $self->pattern;
131 return $pattern->defaults unless @_;
132 my ($shortcut, %defaults) = Mojo::Util::_options(@_);
133
134 if ($shortcut) {
135
136 # Application
137 if (ref $shortcut || $shortcut =~ /^[\w:]+$/) {
138 $defaults{app} = $shortcut;
139 }
140
141 # Controller and action
142 elsif ($shortcut =~ /^([\w\-:]+)?\#(\w+)?$/) {
143 $defaults{controller} = $1 if defined $1;
144 $defaults{action} = $2 if defined $2;
145 }
146 }
147
148 @{$pattern->defaults}{keys %defaults} = values %defaults;
149
150 return $self;
151}
152
153sub to_string {
154 join '', map { $_->pattern->unparsed // '' } @{shift->_chain};
155}
156
157sub under { shift->_generate_route(under => @_) }
158
159sub via {
160 my $self = shift;
161 return $self->{via} unless @_;
162 my $methods = [map uc($_), @{ref $_[0] ? $_[0] : [@_]}];
163 $self->{via} = $methods if @$methods;
164 return $self;
165}
166
167sub websocket {
168 my $route = shift->get(@_);
169 $route->{websocket} = 1;
170 return $route;
171}
172
173sub _chain {
174 my @chain = (my $parent = shift);
175 unshift @chain, $parent while $parent = $parent->parent;
176 return \@chain;
177}
178
179sub _generate_route {
180 my ($self, $methods, @args) = @_;
181
182 my (@conditions, @constraints, %defaults, $name, $pattern);
183 while (defined(my $arg = shift @args)) {
184
185 # First scalar is the pattern
186 if (!ref $arg && !$pattern) { $pattern = $arg }
187
188 # Scalar
189 elsif (!ref $arg && @args) { push @conditions, $arg, shift @args }
190
191 # Last scalar is the route name
192 elsif (!ref $arg) { $name = $arg }
193
194 # Callback
195 elsif (ref $arg eq 'CODE') { $defaults{cb} = $arg }
196
197 # Constraints
198 elsif (ref $arg eq 'ARRAY') { push @constraints, @$arg }
199
200 # Defaults
201 elsif (ref $arg eq 'HASH') { %defaults = (%defaults, %$arg) }
202 }
203
204 my $route
205 = $self->route($pattern, @constraints)->over(\@conditions)->to(\%defaults);
206 $methods eq 'under' ? $route->inline(1) : $route->via($methods);
207
208 return defined $name ? $route->name($name) : $route;
209}
210
211sub _index {
212 my $self = shift;
213
214 my (%auto, %custom);
215 my @children = (@{$self->children});
216 while (my $child = shift @children) {
217 if ($child->has_custom_name) { $custom{$child->name} ||= $child }
218 else { $auto{$child->name} ||= $child }
219 push @children, @{$child->children};
220 }
221
222 return {%auto, %custom};
223}
224
2251;
226
227=encoding utf8
228
229=head1 NAME
230
231Mojolicious::Routes::Route - Route
232
233=head1 SYNOPSIS
234
235 use Mojolicious::Routes::Route;
236
237 my $r = Mojolicious::Routes::Route->new;
238
239=head1 DESCRIPTION
240
241L<Mojolicious::Routes::Route> is the route container used by
242L<Mojolicious::Routes>.
243
244=head1 ATTRIBUTES
245
246L<Mojolicious::Routes::Route> implements the following attributes.
247
248=head2 children
249
250 my $children = $r->children;
251 $r = $r->children([Mojolicious::Routes::Route->new]);
252
253The children of this route, used for nesting routes.
254
255=head2 inline
256
257 my $bool = $r->inline;
258 $r = $r->inline($bool);
259
260Allow L</"under"> semantics for this route.
261
262=head2 parent
263
264 my $parent = $r->parent;
265 $r = $r->parent(Mojolicious::Routes::Route->new);
266
267The parent of this route, usually a L<Mojolicious::Routes::Route> object.
268
269=head2 partial
270
271 my $bool = $r->partial;
272 $r = $r->partial($bool);
273
274Route has no specific end, remaining characters will be captured in C<path>.
275
276=head2 pattern
277
278 my $pattern = $r->pattern;
279 $r = $r->pattern(Mojolicious::Routes::Pattern->new);
280
281Pattern for this route, defaults to a L<Mojolicious::Routes::Pattern> object.
282
283=head1 METHODS
284
285L<Mojolicious::Routes::Route> inherits all methods from L<Mojo::Base> and
286implements the following new ones.
287
288=head2 add_child
289
290 $r = $r->add_child(Mojolicious::Routes::Route->new);
291
292Add a child to this route, it will be automatically removed from its current
293parent if necessary.
294
295 # Reattach route
296 $r->add_child($r->find('foo'));
297
298=head2 any
299
300 my $route = $r->any;
301 my $route = $r->any('/:foo');
302 my $route = $r->any('/:foo' => sub {...});
303 my $route = $r->any('/:foo' => sub {...} => 'name');
304 my $route = $r->any('/:foo' => {foo => 'bar'} => sub {...});
305 my $route = $r->any('/:foo' => [foo => qr/\w+/] => sub {...});
306 my $route = $r->any('/:foo' => (agent => qr/Firefox/) => sub {...});
307 my $route = $r->any(['GET', 'POST'] => '/:foo' => sub {...});
308 my $route = $r->any(['GET', 'POST'] => '/:foo' => [foo => qr/\w+/]);
309
310Generate L<Mojolicious::Routes::Route> object matching any of the listed HTTP
311request methods or all.
312
313 # Route with pattern and destination
314 $r->any('/user')->to('user#whatever');
315
316All arguments are optional, but some have to appear in a certain order, like the
317two supported array reference values, which contain the HTTP methods to match
318and restrictive placeholders.
319
320 # Route with HTTP methods, pattern, restrictive placeholders and destination
321 $r->any(['DELETE', 'PUT'] => '/:foo' => [foo => qr/\w+/])->to('foo#bar');
322
323There are also two supported string values, containing the route pattern and the
324route name, defaulting to the pattern C</> and a name based on the pattern.
325
326 # Route with pattern, name and destination
327 $r->any('/:foo' => 'foo_route')->to('foo#bar');
328
329An arbitrary number of key/value pairs in between the route pattern and name can
330be used to specify route conditions.
331
332 # Route with pattern, condition and destination
333 $r->any('/' => (agent => qr/Firefox/))->to('foo#bar');
334
335A hash reference is used to specify optional placeholders and default values for
336the stash.
337
338 # Route with pattern, optional placeholder and destination
339 $r->any('/:foo' => {foo => 'bar'})->to('foo#bar');
340
341And a code reference can be used to specify a C<cb> value to be merged into the
342default values for the stash.
343
344 # Route with pattern and a closure as destination
345 $r->any('/:foo' => sub {
346 my $c = shift;
347 $c->render(text => 'Hello World!');
348 });
349
350See L<Mojolicious::Guides::Tutorial> and L<Mojolicious::Guides::Routing> for
351more information.
352
353=head2 delete
354
355 my $route = $r->delete;
356 my $route = $r->delete('/:foo');
357 my $route = $r->delete('/:foo' => sub {...});
358 my $route = $r->delete('/:foo' => sub {...} => 'name');
359 my $route = $r->delete('/:foo' => {foo => 'bar'} => sub {...});
360 my $route = $r->delete('/:foo' => [foo => qr/\w+/] => sub {...});
361 my $route = $r->delete('/:foo' => (agent => qr/Firefox/) => sub {...});
362
363Generate L<Mojolicious::Routes::Route> object matching only C<DELETE> requests,
364takes the same arguments as L</"any"> (except for the HTTP methods to match,
365which are implied). See L<Mojolicious::Guides::Tutorial> and
366L<Mojolicious::Guides::Routing> for more information.
367
368 # Route with destination
369 $r->delete('/user')->to('user#remove');
370
371=head2 detour
372
373 $r = $r->detour(action => 'foo');
374 $r = $r->detour('controller#action');
375 $r = $r->detour(Mojolicious->new, foo => 'bar');
376 $r = $r->detour('MyApp', {foo => 'bar'});
377
378Set default parameters for this route and allow partial matching to simplify
379application embedding, takes the same arguments as L</"to">.
380
381=head2 find
382
383 my $route = $r->find('foo');
384
385Find child route by name, custom names have precedence over automatically
386generated ones.
387
388 # Change default parameters of a named route
389 $r->find('show_user')->to(foo => 'bar');
390
391=head2 get
392
393 my $route = $r->get;
394 my $route = $r->get('/:foo');
395 my $route = $r->get('/:foo' => sub {...});
396 my $route = $r->get('/:foo' => sub {...} => 'name');
397 my $route = $r->get('/:foo' => {foo => 'bar'} => sub {...});
398 my $route = $r->get('/:foo' => [foo => qr/\w+/] => sub {...});
399 my $route = $r->get('/:foo' => (agent => qr/Firefox/) => sub {...});
400
401Generate L<Mojolicious::Routes::Route> object matching only C<GET> requests,
402takes the same arguments as L</"any"> (except for the HTTP methods to match,
403which are implied). See L<Mojolicious::Guides::Tutorial> and
404L<Mojolicious::Guides::Routing> for more information.
405
406 # Route with destination
407 $r->get('/user')->to('user#show');
408
409=head2 has_custom_name
410
411 my $bool = $r->has_custom_name;
412
413Check if this route has a custom name.
414
415=head2 has_websocket
416
417 my $bool = $r->has_websocket;
418
419Check if this route has a WebSocket ancestor and cache the result for future
420checks.
421
422=head2 is_endpoint
423
424 my $bool = $r->is_endpoint;
425
426Check if this route qualifies as an endpoint.
427
428=head2 is_websocket
429
430 my $bool = $r->is_websocket;
431
432Check if this route is a WebSocket.
433
434=head2 name
435
436 my $name = $r->name;
437 $r = $r->name('foo');
438
439The name of this route, defaults to an automatically generated name based on
440the route pattern. Note that the name C<current> is reserved for referring to
441the current route.
442
443 # Route with destination and custom name
444 $r->get('/user')->to('user#show')->name('show_user');
445
446=head2 options
447
448 my $route = $r->options;
449 my $route = $r->options('/:foo');
450 my $route = $r->options('/:foo' => sub {...});
451 my $route = $r->options('/:foo' => sub {...} => 'name');
452 my $route = $r->options('/:foo' => {foo => 'bar'} => sub {...});
453 my $route = $r->options('/:foo' => [foo => qr/\w+/] => sub {...});
454 my $route = $r->options('/:foo' => (agent => qr/Firefox/) => sub {...});
455
456Generate L<Mojolicious::Routes::Route> object matching only C<OPTIONS>
457requests, takes the same arguments as L</"any"> (except for the HTTP methods to
458match, which are implied). See L<Mojolicious::Guides::Tutorial> and
459L<Mojolicious::Guides::Routing> for more information.
460
461 # Route with destination
462 $r->options('/user')->to('user#overview');
463
464=head2 over
465
466 my $over = $r->over;
467 $r = $r->over(foo => 1);
468 $r = $r->over(foo => 1, bar => {baz => 'yada'});
469 $r = $r->over([foo => 1, bar => {baz => 'yada'}]);
470
471Activate conditions for this route. Note that this automatically disables the
472routing cache, since conditions are too complex for caching.
473
474 # Route with condition and destination
475 $r->get('/foo')->over(host => qr/mojolicious\.org/)->to('foo#bar');
476
477=head2 parse
478
479 $r = $r->parse('/:action');
480 $r = $r->parse('/:action', action => qr/\w+/);
481 $r = $r->parse(format => 0);
482
483Parse pattern.
484
485=head2 patch
486
487 my $route = $r->patch;
488 my $route = $r->patch('/:foo');
489 my $route = $r->patch('/:foo' => sub {...});
490 my $route = $r->patch('/:foo' => sub {...} => 'name');
491 my $route = $r->patch('/:foo' => {foo => 'bar'} => sub {...});
492 my $route = $r->patch('/:foo' => [foo => qr/\w+/] => sub {...});
493 my $route = $r->patch('/:foo' => (agent => qr/Firefox/) => sub {...});
494
495Generate L<Mojolicious::Routes::Route> object matching only C<PATCH> requests,
496takes the same arguments as L</"any"> (except for the HTTP methods to match,
497which are implied). See L<Mojolicious::Guides::Tutorial> and
498L<Mojolicious::Guides::Routing> for more information.
499
500 # Route with destination
501 $r->patch('/user')->to('user#update');
502
503=head2 post
504
505 my $route = $r->post;
506 my $route = $r->post('/:foo');
507 my $route = $r->post('/:foo' => sub {...});
508 my $route = $r->post('/:foo' => sub {...} => 'name');
509 my $route = $r->post('/:foo' => {foo => 'bar'} => sub {...});
510 my $route = $r->post('/:foo' => [foo => qr/\w+/] => sub {...});
511 my $route = $r->post('/:foo' => (agent => qr/Firefox/) => sub {...});
512
513Generate L<Mojolicious::Routes::Route> object matching only C<POST> requests,
514takes the same arguments as L</"any"> (except for the HTTP methods to match,
515which are implied). See L<Mojolicious::Guides::Tutorial> and
516L<Mojolicious::Guides::Routing> for more information.
517
518 # Route with destination
519 $r->post('/user')->to('user#create');
520
521=head2 put
522
523 my $route = $r->put;
524 my $route = $r->put('/:foo');
525 my $route = $r->put('/:foo' => sub {...});
526 my $route = $r->put('/:foo' => sub {...} => 'name');
527 my $route = $r->put('/:foo' => {foo => 'bar'} => sub {...});
528 my $route = $r->put('/:foo' => [foo => qr/\w+/] => sub {...});
529 my $route = $r->put('/:foo' => (agent => qr/Firefox/) => sub {...});
530
531Generate L<Mojolicious::Routes::Route> object matching only C<PUT> requests,
532takes the same arguments as L</"any"> (except for the HTTP methods to match,
533which are implied). See L<Mojolicious::Guides::Tutorial> and
534L<Mojolicious::Guides::Routing> for more information.
535
536 # Route with destination
537 $r->put('/user')->to('user#replace');
538
539=head2 remove
540
541 $r = $r->remove;
542
543Remove route from parent.
544
545 # Remove route completely
546 $r->find('foo')->remove;
547
548 # Reattach route to new parent
549 $r->route('/foo')->add_child($r->find('bar')->remove);
550
551=head2 render
552
553 my $path = $r->render({foo => 'bar'});
554
555Render route with parameters into a path.
556
557=head2 root
558
559 my $root = $r->root;
560
561The L<Mojolicious::Routes> object this route is a descendant of.
562
563=head2 route
564
565 my $route = $r->route;
566 my $route = $r->route('/:action');
567 my $route = $r->route('/:action', action => qr/\w+/);
568 my $route = $r->route(format => 0);
569
570Low-level generator for routes matching all HTTP request methods, returns a
571L<Mojolicious::Routes::Route> object.
572
573=head2 suggested_method
574
575 my $method = $r->suggested_method;
576
577Suggested HTTP method for reaching this route, C<GET> and C<POST> are
578preferred.
579
580=head2 to
581
582 my $defaults = $r->to;
583 $r = $r->to(action => 'foo');
584 $r = $r->to({action => 'foo'});
585 $r = $r->to('controller#action');
586 $r = $r->to('controller#action', foo => 'bar');
587 $r = $r->to('controller#action', {foo => 'bar'});
588 $r = $r->to(Mojolicious->new);
589 $r = $r->to(Mojolicious->new, foo => 'bar');
590 $r = $r->to(Mojolicious->new, {foo => 'bar'});
591 $r = $r->to('MyApp');
592 $r = $r->to('MyApp', foo => 'bar');
593 $r = $r->to('MyApp', {foo => 'bar'});
594
595Set default parameters for this route.
596
597=head2 to_string
598
599 my $str = $r->to_string;
600
601Stringify the whole route.
602
603=head2 under
604
605 my $route = $r->under(sub {...});
606 my $route = $r->under('/:foo' => sub {...});
607 my $route = $r->under('/:foo' => {foo => 'bar'});
608 my $route = $r->under('/:foo' => [foo => qr/\w+/]);
609 my $route = $r->under('/:foo' => (agent => qr/Firefox/));
610 my $route = $r->under([format => 0]);
611
612Generate L<Mojolicious::Routes::Route> object for a nested route with its own
613intermediate destination, takes the same arguments as L</"any"> (except for the
614HTTP methods to match, which are not available). See
615L<Mojolicious::Guides::Tutorial> and L<Mojolicious::Guides::Routing> for more
616information.
617
618 # Intermediate destination and prefix shared between two routes
619 my $auth = $r->under('/user')->to('user#auth');
620 $auth->get('/show')->to('#show');
621 $auth->post('/create')->to('#create');
622
623=head2 via
624
625 my $methods = $r->via;
626 $r = $r->via('GET');
627 $r = $r->via('GET', 'POST');
628 $r = $r->via(['GET', 'POST']);
629
630Restrict HTTP methods this route is allowed to handle, defaults to no
631restrictions.
632
633 # Route with two methods and destination
634 $r->route('/foo')->via('GET', 'POST')->to('foo#bar');
635
636=head2 websocket
637
638 my $route = $r->websocket;
639 my $route = $r->websocket('/:foo');
640 my $route = $r->websocket('/:foo' => sub {...});
641 my $route = $r->websocket('/:foo' => sub {...} => 'name');
642 my $route = $r->websocket('/:foo' => {foo => 'bar'} => sub {...});
643 my $route = $r->websocket('/:foo' => [foo => qr/\w+/] => sub {...});
644 my $route = $r->websocket('/:foo' => (agent => qr/Firefox/) => sub {...});
645
646Generate L<Mojolicious::Routes::Route> object matching only WebSocket
647handshakes, takes the same arguments as L</"any"> (except for the HTTP methods
648to match, which are implied). See L<Mojolicious::Guides::Tutorial> and
649L<Mojolicious::Guides::Routing> for more information.
650
651 # Route with destination
652 $r->websocket('/echo')->to('example#echo');
653
654=head1 AUTOLOAD
655
656In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
657shortcuts provided by L</"root"> on L<Mojolicious::Routes::Route> objects.
658
659 # Add a "firefox" shortcut
660 $r->root->add_shortcut(firefox => sub {
661 my ($r, $path) = @_;
662 $r->get($path, agent => qr/Firefox/);
663 });
664
665 # Use "firefox" shortcut to generate routes
666 $r->firefox('/welcome')->to('firefox#welcome');
667 $r->firefox('/bye')->to('firefox#bye');
668
669=head1 SEE ALSO
670
671L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
672
673=cut
Note: See TracBrowser for help on using the repository browser.