File Coverage

blib/lib/Mojolicious/Plugin/AutoRoutePm.pm
Criterion Covered Total %
statement 65 71 91.5
branch 8 16 50.0
condition 4 8 50.0
subroutine 6 6 100.0
pod 1 3 33.3
total 84 104 80.7


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::AutoRoutePm;
2             $Mojolicious::Plugin::AutoRoutePm::VERSION = '0.23';
3 2     2   2888 use Mojo::Base 'Mojolicious::Plugin';
  2         5  
  2         16  
4              
5 2     2   1663 use File::Find::Rule;
  2         18488  
  2         22  
6 2     2   155 use Mojo::Loader qw(load_class);
  2         5  
  2         2481  
7              
8             sub register {
9 2     2 1 130 my ( $self, $app, $conf ) = @_;
10              
11             # default index
12 2   50     17 my $dindex = $conf->{default_index} || 'index';
13              
14             # Parent route
15 2   50     8 my $r = $conf->{route} || [ $app->routes ];
16              
17             # Excluded routes
18 2   50     11 my $exclude = $conf->{exclude} || [];
19              
20             # my %exclude = map {$_ => 1} @$exclude;
21              
22             # Template Base
23 2         13 my $system_template_base_dirs = $app->renderer->paths;
24              
25             # by default renderer->paths appends templates to base_app_path
26             # removing it we got the base path
27 2         94 my $template_base_dirs = [];
28 2         6 foreach (@$system_template_base_dirs) {
29 4         11 my $tmpl_base_dir = $_; # so next replace doesn't affect origianl path
30 4         18 $tmpl_base_dir =~ s/templates$//;
31 4         11 push @$template_base_dirs, $tmpl_base_dir;
32             }
33              
34             # Top directory
35 2   50     9 my $top_dir = $conf->{top_dir} || '.';
36 2         6 $top_dir =~ s#^/##;
37 2         4 $top_dir =~ s#/$##;
38              
39             # Search templates
40 2         6 my @templates;
41 2         5 for my $template_base_dir (@$template_base_dirs) {
42 4         40 $template_base_dir =~ s#/$##;
43 4         14 my $template_dir = "$template_base_dir/$top_dir";
44              
45 4 100       120 if ( -d $template_dir ) {
46              
47             # Find templates
48 2         63 my $rules = File::Find::Rule->file()->name('*.pm')->relative(0)
49             ->start($template_dir);
50 2         3141 while ( defined( my $file = $rules->match ) ) {
51 6         87 $file =~ s/\.pm$//;
52 6         14 my $excluded = 0;
53 6         12 foreach (@$exclude) {
54 0 0       0 $excluded = 1 if ( $file =~ /$_/ );
55             }
56 6 50       31 push @templates, $file unless ($excluded);
57             }
58              
59             }
60             }
61              
62             # arrange templates so first existing .pm module which aren't DocumentRoot
63             # sort so longer path come first so existing subfolder have major priority
64 2         64 my @template_index = sort { length($b) cmp length($a) } grep /$dindex$/,
  2         17  
65             @templates;
66 2         50 my @template_nindex = sort { length($b) cmp length($a) } grep !/$dindex$/,
  0         0  
67             @templates;
68 2         9 @templates = ( @template_nindex, @template_index );
69              
70             # Register routes
71 2         5 for my $template (@templates) {
72              
73             # Route
74 6         642 my $ctl = $self->path_to_controller($template);
75 6         35 load_class $ctl;
76 6 50       5676 if ( $ctl->isa('Mojolicious::Controller') ) {
77 6         28 $template = "/$template";
78 6         23 my $route = $self->get_best_matched_route( $template, $r );
79 6         14 my $routep = $route->to_string;
80 6         146 $template =~ s/^$routep//;
81              
82             # support for /url_component/index
83 6         37 my $tr = $route->any( $template => [ format => 1 ] )
84             ->to( app => $ctl, action => 'route', format => undef );
85 6         2826 $tr->any('');
86              
87             # and for /url_component/index/a/b/x
88 6         1172 $tr->any('/*query');
89 6 100       1819 if ( $template =~ s/$dindex$// ) {
90              
91             # support for /url_component
92 4         19 my $tr = $route->any( $template => [ format => 1 ] )
93             ->to( app => $ctl, action => 'route', format => undef );
94 4         1331 $tr->any('/');
95              
96             # and for /url_component/a/b/x
97 4         732 $tr->any('/*query');
98             }
99             }
100             }
101             }
102              
103             sub get_best_matched_route {
104 6     6 0 13 my $s = shift;
105 6         14 my $url = shift;
106              
107 6         11 my $routes = shift;
108              
109 6         12 my @ret;
110              
111 6         17 foreach my $r (@$routes) {
112 6 50       59 push @ret, $r
113             if ( substr( $url, 0, length( $r->to_string ) ) eq $r->to_string );
114             }
115              
116 6 50       415 return $ret[0] if ( scalar(@ret) == 1 ); # only one
117              
118             # more than one
119 0         0 my $ret = $ret[0];
120 0         0 foreach my $r (@ret) {
121 0 0       0 $ret = $r if ( length( $r->name ) > length( $ret->name ) );
122             }
123 0         0 return $ret;
124             }
125              
126             sub path_to_controller {
127 6     6 0 13 my $s = shift;
128 6         13 my $url = shift;
129              
130 6         13 $url =~ s{^/}{};
131 6         23 $url =~ s{/}{::}g;
132 6         19 $url =~ s{\.(.*?)$}{};
133              
134 6         13 return $url;
135             }
136              
137             1;
138              
139             =pod
140              
141             =head1 NAME
142              
143             Mojolicious::Plugin::AutoRoutePm - Mojolicious plugin to create routes by *.pm modules which are a controller
144              
145             =for html

146            
147             github workflow tests
148            
149             Top language:
150             github last commit
151            

152              
153             =head1 VERSION
154              
155             version 0.23
156              
157             =head1 SYNOPSIS
158              
159             use Mojolicious::Lite;
160             plugin 'AutoRoutePm', {
161             route => [ app->routes ],
162             top_dir => 'site',
163             };
164              
165             =head1 DESCRIPTION
166              
167             This module recursive passes through template_base_dir to find perl modules
168             (*.pm) that are a subclass of Mojolicious::Controller and adds routes for them.
169              
170             =encoding UTF-8
171              
172             =head1 USAGE
173              
174             For module X::Y::Z it adds the decamelize version
175              
176             x/y/z
177             x/y/z/index
178             x/y/z/index/*query
179              
180             all redirect to action route inside module.
181              
182             If Z is default_index it adds also
183              
184             x/y
185             x/y/*query
186              
187             The last structure is useful for routing seach. But be careful to correct
188             relative urls of other items in html page.
189              
190             This can be done in many ways. One is, as an example, to add to the layout
191             a base_url like this
192              
193             % my $base_url = url_for(undef, {query => undef}); $base_url =~ s|/$||;
194            
195              
196             =head2 register
197              
198             plugin->register($app);
199              
200             Register plugin in L application.
201              
202             =head1 BUGS/CONTRIBUTING
203              
204             Please report any bugs through the web interface at L
205             If you want to contribute changes or otherwise involve yourself in development, feel free to fork the Git repository from
206             L.
207              
208             =head1 SUPPORT
209              
210             You can find this documentation with the perldoc command too.
211              
212             perldoc Mojo::WebSocket::PubSub
213              
214             =head1 SEE ALSO
215              
216             L, L, L.
217              
218             =head1 AUTHOR
219              
220             Emiliano Bruni
221              
222             =head1 COPYRIGHT AND LICENSE
223              
224             This software is copyright (c) 2021 by Emiliano Bruni.
225              
226             This is free software; you can redistribute it and/or modify it under
227             the same terms as the Perl 5 programming language system itself.
228              
229             =cut
230              
231             __END__