source: main/trunk/greenstone2/perllib/cpan/Mojolicious/Routes.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: 9.2 KB
Line 
1package Mojolicious::Routes;
2use Mojo::Base 'Mojolicious::Routes::Route';
3
4use List::Util 'first';
5use Mojo::Cache;
6use Mojo::Loader 'load_class';
7use Mojo::Util 'camelize';
8use Mojolicious::Routes::Match;
9use Scalar::Util 'weaken';
10
11has base_classes => sub { [qw(Mojolicious::Controller Mojolicious)] };
12has cache => sub { Mojo::Cache->new };
13has [qw(conditions shortcuts)] => sub { {} };
14has types => sub { {num => qr/[0-9]+/} };
15has hidden => sub { [qw(attr has new tap)] };
16has namespaces => sub { [] };
17
18sub add_condition { $_[0]->conditions->{$_[1]} = $_[2] and return $_[0] }
19sub add_shortcut { $_[0]->shortcuts->{$_[1]} = $_[2] and return $_[0] }
20sub add_type { $_[0]->types->{$_[1]} = $_[2] and return $_[0] }
21
22sub continue {
23 my ($self, $c) = @_;
24
25 my $match = $c->match;
26 my $stack = $match->stack;
27 my $position = $match->position;
28 return _render($c) unless my $field = $stack->[$position];
29
30 # Merge captures into stash
31 my $stash = $c->stash;
32 @{$stash->{'mojo.captures'} //= {}}{keys %$field} = values %$field;
33 @$stash{keys %$field} = values %$field;
34
35 my $continue;
36 my $last = !$stack->[++$position];
37 if (my $cb = $field->{cb}) { $continue = $self->_callback($c, $cb, $last) }
38 else { $continue = $self->_controller($c, $field, $last) }
39 $match->position($position);
40 $self->continue($c) if $last || $continue;
41}
42
43sub dispatch {
44 my ($self, $c) = @_;
45 $self->match($c);
46 @{$c->match->stack} ? $self->continue($c) : return undef;
47 return 1;
48}
49
50sub hide { push @{shift->hidden}, @_ }
51
52sub is_hidden {
53 my ($self, $method) = @_;
54 my $h = $self->{hiding} ||= {map { $_ => 1 } @{$self->hidden}};
55 return !!($h->{$method} || $method =~ /^_/ || $method =~ /^[A-Z_]+$/);
56}
57
58sub lookup { ($_[0]{reverse} //= $_[0]->_index)->{$_[1]} }
59
60sub match {
61 my ($self, $c) = @_;
62
63 # Path (partial path gets priority)
64 my $req = $c->req;
65 my $path = $c->stash->{path};
66 if (defined $path) { $path = "/$path" if $path !~ m!^/! }
67 else { $path = $req->url->path->to_route }
68
69 # Method (HEAD will be treated as GET)
70 my $method = uc($req->url->query->clone->param('_method') || $req->method);
71 $method = 'GET' if $method eq 'HEAD';
72
73 # Check cache
74 my $ws = $c->tx->is_websocket ? 1 : 0;
75 my $match = Mojolicious::Routes::Match->new(root => $self);
76 $c->match($match);
77 my $cache = $self->cache;
78 if (my $result = $cache->get("$method:$path:$ws")) {
79 return $match->endpoint($result->{endpoint})->stack($result->{stack});
80 }
81
82 # Check routes
83 $match->find($c => {method => $method, path => $path, websocket => $ws});
84 return unless my $route = $match->endpoint;
85 $cache->set(
86 "$method:$path:$ws" => {endpoint => $route, stack => $match->stack});
87}
88
89sub _action { shift->plugins->emit_chain(around_action => @_) }
90
91sub _callback {
92 my ($self, $c, $cb, $last) = @_;
93 $c->stash->{'mojo.routed'} = 1 if $last;
94 my $app = $c->app;
95 $app->log->debug('Routing to a callback');
96 return _action($app, $c, $cb, $last);
97}
98
99sub _class {
100 my ($self, $c, $field) = @_;
101
102 # Application instance
103 return $field->{app} if ref $field->{app};
104
105 # Application class
106 my @classes;
107 my $class = $field->{controller} ? camelize $field->{controller} : '';
108 if ($field->{app}) { push @classes, $field->{app} }
109
110 # Specific namespace
111 elsif (defined(my $ns = $field->{namespace})) {
112 if ($class) { push @classes, $ns ? "${ns}::$class" : $class }
113 elsif ($ns) { push @classes, $ns }
114 }
115
116 # All namespaces
117 elsif ($class) { push @classes, "${_}::$class" for @{$self->namespaces} }
118
119 # Try to load all classes
120 my $log = $c->app->log;
121 for my $class (@classes) {
122
123 # Failed
124 next unless defined(my $found = $self->_load($class));
125 return !$log->debug(qq{Class "$class" is not a controller}) unless $found;
126
127 # Success
128 my $new = $class->new(%$c);
129 weaken $new->{$_} for qw(app tx);
130 return $new;
131 }
132
133 # Nothing found
134 $log->debug(qq{Controller "$classes[-1]" does not exist}) if @classes;
135 return @classes ? undef : 0;
136}
137
138sub _controller {
139 my ($self, $old, $field, $last) = @_;
140
141 # Load and instantiate controller/application
142 my $new;
143 unless ($new = $self->_class($old, $field)) { return defined $new }
144
145 # Application
146 my $class = ref $new;
147 my $app = $old->app;
148 my $log = $app->log;
149 if ($new->isa('Mojolicious')) {
150 $log->debug(qq{Routing to application "$class"});
151
152 # Try to connect routes
153 if (my $sub = $new->can('routes')) {
154 my $r = $new->$sub;
155 weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent;
156 }
157 $new->handler($old);
158 $old->stash->{'mojo.routed'} = 1;
159 }
160
161 # Action
162 elsif (my $method = $field->{action}) {
163 if (!$self->is_hidden($method)) {
164 $log->debug(qq{Routing to controller "$class" and action "$method"});
165
166 if (my $sub = $new->can($method)) {
167 $old->stash->{'mojo.routed'} = 1 if $last;
168 return 1 if _action($app, $new, $sub, $last);
169 }
170
171 else { $log->debug('Action not found in controller') }
172 }
173 else { $log->debug(qq{Action "$method" is not allowed}) }
174 }
175
176 return undef;
177}
178
179sub _load {
180 my ($self, $app) = @_;
181
182 # Load unless already loaded
183 return 1 if $self->{loaded}{$app};
184 if (my $e = load_class $app) { ref $e ? die $e : return undef }
185
186 # Check base classes
187 return 0 unless first { $app->isa($_) } @{$self->base_classes};
188 return $self->{loaded}{$app} = 1;
189}
190
191sub _render {
192 my $c = shift;
193 my $stash = $c->stash;
194 return if $stash->{'mojo.rendered'};
195 $c->render_maybe or $stash->{'mojo.routed'} or $c->helpers->reply->not_found;
196}
197
1981;
199
200=encoding utf8
201
202=head1 NAME
203
204Mojolicious::Routes - Always find your destination with routes
205
206=head1 SYNOPSIS
207
208 use Mojolicious::Routes;
209
210 # Simple route
211 my $r = Mojolicious::Routes->new;
212 $r->route('/')->to(controller => 'blog', action => 'welcome');
213
214 # More advanced routes
215 my $blog = $r->under('/blog');
216 $blog->get('/list')->to('blog#list');
217 $blog->get('/:id' => [id => qr/\d+/])->to('blog#show', id => 23);
218 $blog->patch(sub { shift->render(text => 'Go away!', status => 405) });
219
220=head1 DESCRIPTION
221
222L<Mojolicious::Routes> is the core of the L<Mojolicious> web framework.
223
224See L<Mojolicious::Guides::Routing> for more.
225
226=head1 TYPES
227
228These placeholder types are available by default.
229
230=head2 num
231
232 $r->get('/article/<id:num>');
233
234Placeholder value needs to be a non-fractional number, similar to the regular
235expression C<([0-9]+)>.
236
237=head1 ATTRIBUTES
238
239L<Mojolicious::Routes> inherits all attributes from
240L<Mojolicious::Routes::Route> and implements the following new ones.
241
242=head2 base_classes
243
244 my $classes = $r->base_classes;
245 $r = $r->base_classes(['MyApp::Controller']);
246
247Base classes used to identify controllers, defaults to
248L<Mojolicious::Controller> and L<Mojolicious>.
249
250=head2 cache
251
252 my $cache = $r->cache;
253 $r = $r->cache(Mojo::Cache->new);
254
255Routing cache, defaults to a L<Mojo::Cache> object.
256
257=head2 conditions
258
259 my $conditions = $r->conditions;
260 $r = $r->conditions({foo => sub {...}});
261
262Contains all available conditions.
263
264=head2 hidden
265
266 my $hidden = $r->hidden;
267 $r = $r->hidden(['attr', 'has', 'new']);
268
269Controller attributes and methods that are hidden from router, defaults to
270C<attr>, C<has>, C<new> and C<tap>.
271
272=head2 namespaces
273
274 my $namespaces = $r->namespaces;
275 $r = $r->namespaces(['MyApp::Controller', 'MyApp']);
276
277Namespaces to load controllers from.
278
279 # Add another namespace to load controllers from
280 push @{$r->namespaces}, 'MyApp::MyController';
281
282=head2 shortcuts
283
284 my $shortcuts = $r->shortcuts;
285 $r = $r->shortcuts({foo => sub {...}});
286
287Contains all available shortcuts.
288
289=head2 types
290
291 my $types = $r->types;
292 $r = $r->types({lower => qr/[a-z]+/});
293
294Registered placeholder types, by default only L</"num"> is already defined.
295
296=head1 METHODS
297
298L<Mojolicious::Routes> inherits all methods from L<Mojolicious::Routes::Route>
299and implements the following new ones.
300
301=head2 add_condition
302
303 $r = $r->add_condition(foo => sub {...});
304
305Register a condition.
306
307 $r->add_condition(foo => sub {
308 my ($route, $c, $captures, $arg) = @_;
309 ...
310 return 1;
311 });
312
313=head2 add_shortcut
314
315 $r = $r->add_shortcut(foo => sub {...});
316
317Register a shortcut.
318
319 $r->add_shortcut(foo => sub {
320 my ($route, @args) = @_;
321 ...
322 });
323
324=head2 add_type
325
326 $r = $r->add_type(foo => qr/\w+/);
327 $r = $r->add_type(foo => ['bar', 'baz']);
328
329Register a placeholder type.
330
331 $r->add_type(lower => qr/[a-z]+/);
332
333=head2 continue
334
335 $r->continue(Mojolicious::Controller->new);
336
337Continue dispatch chain and emit the hook L<Mojolicious/"around_action"> for
338every action.
339
340=head2 dispatch
341
342 my $bool = $r->dispatch(Mojolicious::Controller->new);
343
344Match routes with L</"match"> and dispatch with L</"continue">.
345
346=head2 hide
347
348 $r = $r->hide('foo', 'bar');
349
350Hide controller attributes and methods from router.
351
352=head2 is_hidden
353
354 my $bool = $r->is_hidden('foo');
355
356Check if controller attribute or method is hidden from router.
357
358=head2 lookup
359
360 my $route = $r->lookup('foo');
361
362Find route by name with L<Mojolicious::Routes::Route/"find"> and cache all
363results for future lookups.
364
365=head2 match
366
367 $r->match(Mojolicious::Controller->new);
368
369Match routes with L<Mojolicious::Routes::Match>.
370
371=head1 SEE ALSO
372
373L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
374
375=cut
Note: See TracBrowser for help on using the repository browser.