File Coverage

blib/lib/Mojolicious/Plugin/RoutesConfig.pm
Criterion Covered Total %
statement 45 47 95.7
branch 20 22 90.9
condition 2 4 50.0
subroutine 6 6 100.0
pod 1 1 100.0
total 74 80 92.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::RoutesConfig;
2 2     2   181301 use Mojo::Base 'Mojolicious::Plugin::Config', -signatures;
  2         224593  
  2         19  
3 2     2   51982 use List::Util qw(first);
  2         7  
  2         2207  
4              
5             our $VERSION = 0.07;
6             our $AUTHORITY = 'cpan:BEROV';
7              
8             sub register {
9 8     8 1 16070 my ($self, $app, $conf) = @_;
10 8         18 my $file = $conf->{file};
11 8 100       29 my $file_msg = ($file ? ' in file ' . $file : '');
12 8         82 $conf = $self->SUPER::register($app, $conf);
13             $app->log->warn('No routes definitions found' . $file_msg . '...')
14             && return $conf
15 8 100 50     6049 unless exists $conf->{routes};
16             $app->log->warn( '"routes" key must point to an ARRAY reference '
17             . 'of routes descriptions'
18             . $file_msg . '...')
19             && return $conf
20 6 100 50     25 unless ref $conf->{routes} eq 'ARRAY';
21              
22 2         9 $self->_generate_routes($app, $app->routes, $conf->{routes}, $file_msg);
23 2         13 return $conf;
24             }
25              
26             # generates routes (recursively for under)
27             sub _generate_routes {
28 4     4   18 my ($self, $app, $routes, $routes_conf, $file_msg) = @_;
29 4         10 my $init_rx = '^(?:any|get|post|patch|put|delete|options|under)$';
30 4         9 for my $rconf (@$routes_conf) {
31 20     31   323 my $init_method = first(sub { $_ =~ /$init_rx/; }, keys %$rconf);
  31         168  
32 20 100       83 unless ($init_method) {
33 2         9 $app->log->warn( "Malformed route description$file_msg!!!$/"
34             . " Could not find route initialisation method, matching$/"
35             . " /$init_rx/$/"
36             . " in definition$/"
37             . $app->dumper($rconf)
38             . ". Skipping...");
39 2         489 next;
40             }
41 18         37 my $init_params = $rconf->{$init_method};
42 18         45 my $route = _call_method($routes, $init_method, $init_params);
43 18 100       5470 if ($init_method eq 'under') { # recourse
44 2         12 $self->_generate_routes($app, $route, $rconf->{routes}, $file_msg);
45             }
46 18         52 for my $method (keys %$rconf) {
47 40 100       871 next if $method =~ /^(?:$init_method|routes)$/;
48 20         48 my $params = $rconf->{$method};
49 20 100       79 $route->can($method) || do {
50 2 50       30 $app->log->warn("Malformed route description$file_msg!!!$/"
51             . " for route definition$/"
52             . $app->dumper($rconf)
53 2         591 . qq|Can't locate object method "$method" via package "${\ ref $route}"!$/|
54             . ' Removing route '
55             . (ref $init_params eq 'ARRAY' ? $init_params->[0] : $init_params));
56 2         175 $route->remove();
57 2         81 last;
58             };
59 18         328 _call_method($route, $method, $params);
60             }
61             }
62 4         12 return;
63             }
64              
65             # Returns a new or existing route
66 36     36   56 sub _call_method ($caller, $method, $params) {
  36         55  
  36         51  
  36         51  
  36         52  
67 36 100       101 if (ref $params eq 'ARRAY') {
    100          
    50          
68 6         21 return $caller->$method(@$params);
69             }
70             elsif (ref $params eq 'HASH') {
71 10         53 return $caller->$method(%$params);
72             }
73             elsif (ref $params eq 'CODE') {
74 0         0 return $caller->$method($params->());
75             }
76             else {
77 20         81 return $caller->$method($params);
78             }
79              
80 0           Carp::confess("Paramether to $method "
81             . "must be one of ARRAY, HASH, CODE reference or scalar in the form controller#action. "
82             . "Now it is "
83             . Mojo::Util::dumper($params));
84             }
85              
86             =encoding utf8
87              
88             =head1 NAME
89              
90             Mojolicious::Plugin::RoutesConfig - Describe routes in configuration
91              
92             =head1 SYNOPSIS
93              
94             # Create $MOJO_HOME/etc/routes.conf and describe your routes
95             # or do it directly in $MOJO_HOME/${\ $app->moniker }.conf
96             {
97             routes => [
98             {get => '/groups', to => 'groups#list', name => 'list_groups'},
99             {post => '/groups', to => 'groups#create'},
100             {any => {[qw(GET POST)] => '/users'}, to => 'users#list_or_create'},
101              
102             {under => '/управление', to => 'auth#under_management',
103             routes => [
104             {any => '/', to => 'upravlenie#index', name => 'home_upravlenie'},
105             {get => '/groups', to => 'groups#index', name => 'home_groups'},
106             #...
107             ],
108             },
109             ],
110             }
111              
112             # in YourApp::startup()
113             my $config = $app->plugin('Config');
114             # or even
115             my $config = $app->plugin('RoutesConfig');
116             # or
117             $app->plugin('RoutesConfig', $config);
118             $app->plugin('RoutesConfig', {file => $app->home->child('etc/routes_admin.conf')});
119             $app->plugin('RoutesConfig', {file => $app->home->child('etc/routes_site.conf')});
120              
121             # in YourLiteApp
122             my $config = plugin 'Config';
123             plugin 'RoutesConfig', $config;
124             plugin 'RoutesConfig', {file => app->home->child('etc/routes_admin.conf')};
125             plugin 'RoutesConfig', {file => app->home->child('etc/routes_site.conf')};
126              
127             =head1 DESCRIPTION
128              
129             L allows you to define your routes in
130             configuration file or in a separate file, for example
131             C<$MOJO_HOME/etc/plugins/routes.conf>. This way you can quickly enable and
132             disable parts of your application without editing its source code.
133              
134             The routes are described the same way as you would generate them imperatively,
135             just instead of methods you use method names as keys and suitable references as
136             values which will be dereferenced and passed as arguments to the respective
137             method. If C<$parameters> is a reference to CODE it will be executed and
138             whatever it returns will be the parameters for the respective method.For
139             allowed keys look at L. Look at
140             C for inspiration. You can have all your routes
141             defined in the configuration file as it is Perl and you have the C object
142             available.
143              
144             =head1 METHODS
145              
146             L inherits all methods from L and implements the following new ones.
147              
148             =head2 register
149              
150             my $config = $plugin->register(Mojolicious->new, $config);
151             my $config = $plugin->register($app, {file => '/etc/app_routes.conf'});
152              
153             Register the plugin in L application and generate routes.
154              
155             =head1 AUTHOR
156              
157             Красимир Беров
158             CPAN ID: BEROV
159             berov ат cpan точка org
160             http://i-can.eu
161              
162             =head1 COPYRIGHT
163              
164             This program is free software; you can redistribute
165             it and/or modify it under the terms of Artistic License 2.0.
166              
167             The full text of the license can be found in the
168             LICENSE file included with this module.
169              
170             =head1 SEE ALSO
171              
172             L, L, L
173              
174             =cut
175              
176             1;
177