File Coverage

blib/lib/Clustericious/RouteBuilder.pm
Criterion Covered Total %
statement 85 101 84.1
branch 30 46 65.2
condition 22 41 53.6
subroutine 21 26 80.7
pod n/a
total 158 214 73.8


line stmt bran cond sub pod time code
1             package Clustericious::RouteBuilder;
2              
3 19     19   5832 use strict;
  19         47  
  19         534  
4 19     19   96 use warnings;
  19         68  
  19         436  
5 19     19   323 use 5.010;
  19         67  
6 19     19   100 use Clustericious::App;
  19         47  
  19         263  
7 19     19   548 use Log::Log4perl qw( :easy );
  19         44  
  19         142  
8 19     19   13738 use Mojo::Util qw( monkey_patch );
  19         41  
  19         17087  
9              
10             # ABSTRACT: Route builder for Clustericious applications
11             our $VERSION = '1.27'; # VERSION
12              
13              
14             my %routes;
15              
16             # Much of the code below taken directly from Mojolicious::Lite.
17             sub import {
18 19     19   71 my($class, $app_class) = @_;
19 19         51 my $caller = caller;
20            
21 19 100       83 unless($app_class)
22             {
23 15 100       177 if ($caller->isa("Clustericious::App"))
24             {
25 6         18 $app_class = $caller;
26             }
27             else
28             {
29 9         19 $app_class = $caller;
30 9 50       60 $app_class =~ s/::Routes$// or die "could not guess app class : ";
31             }
32             }
33              
34 19         47 my @routes;
35 19         54 $routes{$app_class} = \@routes;
36              
37             # Route generator
38             my $route_sub = sub
39             {
40 46     46   133 my ($methods, @args) = @_;
41              
42 46         149 my ($cb, $constraints, $defaults, $name, $pattern);
43 46         111 my $conditions = [];
44              
45             # Route information
46 46         77 my $condition;
47 46         235 while (my $arg = shift @args)
48             {
49              
50             # Condition can be everything
51 83 50 100     502 if ($condition)
    100 66        
    50          
    100          
    50          
    0          
    0          
52             {
53 0         0 push @$conditions, $condition => $arg;
54 0         0 $condition = undef;
55             }
56              
57             # First scalar is the pattern
58             elsif (!ref $arg && !$pattern)
59             {
60 38         122 $pattern = $arg;
61             }
62              
63             # Scalar
64             elsif (!ref $arg && @args)
65             {
66 0         0 $condition = $arg;
67             }
68              
69             # Last scalar is the route name
70             elsif (!ref $arg)
71             {
72 9         30 $name = $arg;
73             }
74              
75             # Callback
76             elsif (ref $arg eq 'CODE')
77             {
78 36         105 $cb = $arg;
79             }
80              
81             # Constraints
82             elsif (ref $arg eq 'ARRAY')
83             {
84 0         0 $constraints = $arg;
85             }
86              
87             # Defaults
88             elsif (ref $arg eq 'HASH')
89             {
90 0         0 $defaults = $arg;
91             }
92             }
93              
94             # Defaults
95 46   100     176 $cb ||= sub {1};
  0         0  
96 46   50     223 $constraints ||= [];
97              
98             # Merge
99 46   50     198 $defaults ||= {};
100 46         176 $defaults = {%$defaults, cb => $cb};
101              
102             # Name
103 46   100     234 $name ||= '';
104              
105 46         264 push @routes, {
106             name => $name,
107             pattern => $pattern,
108             constraints => $constraints,
109             conditions => $conditions,
110             defaults => $defaults,
111             methods => $methods
112             };
113              
114 19         111 };
115              
116             # Export
117 19         125 monkey_patch $app_class, startup_route_builder => \&_startup_route_builder;
118              
119 19 0   0   540 monkey_patch $caller, any => sub { $route_sub->(ref $_[0] ? shift : [], @_) };
  0     0   0  
120 19     0   361 monkey_patch $caller, $_ => sub { unshift @_, 'delete'; goto $route_sub } for qw( Delete del );
  0     0   0  
  0     0   0  
121              
122 19         559 foreach my $method (qw( get head ladder post put websocket authenticate authorize ))
123             {
124 152     46   2187 monkey_patch $caller, $method => sub { unshift @_, $method; goto $route_sub };
  46     46   139  
  46     46   122  
        46      
        46      
        46      
        46      
        46      
        46      
125             }
126             }
127              
128             sub _startup_route_builder {
129 21     21   78 my($app, $auth_plugin) = @_;
130              
131 21   33     103 my $stashed = $routes{ ref $app } // do { WARN "no routes stashed for $app"; [] };
  0         0  
  0         0  
132 21         72 my @stashed = @$stashed;
133 21         89 my $routes = $app->routes;
134 21         134 my $head_route = $app->routes;
135 21         90 my $head_authenticated = $head_route;
136              
137 21         61 for my $spec (@stashed)
138             {
139             my ($name,$pattern,$constraints,$conditions,$defaults,$methods) =
140 54         347 @$spec{qw/name pattern constraints conditions defaults methods/};
141              
142             # authenticate, always connects to app->routes
143 54 100 66     256 if (!ref $methods && $methods eq 'authenticate')
144             {
145 5   33     28 my $realm = $pattern || ref $app;
146 5 100   13   31 my $cb = defined $auth_plugin ? sub { $auth_plugin->authenticate(shift, $realm) } : sub { 1 };
  26         1817  
  1         67  
147 5         19 $head_route = $head_authenticated = $routes =
148             $app->routes->under->to( { cb => $cb } )->name("authenticated");
149 5         730 next;
150             }
151              
152             # authorize replaces previous authorize's
153 49 100 66     212 if (!ref $methods && $methods eq 'authorize')
154             {
155 4 50       22 die "put authenticate before authorize" unless $head_authenticated;
156 4         9 my $action = $pattern;
157 4         6 my $resource = $name;
158 4 100       15 if($auth_plugin)
159             {
160             $head_route = $routes = $head_authenticated->under->to( {
161             cb => sub {
162 10     10   819 my($c) = @_;
163             # Dynamically compute resource/action
164 10         37 my ($d_resource,$d_action) = ($resource, $action);
165 10 50       43 $d_resource =~ s/<path>/$c->req->url->path/e if $d_resource;
  0         0  
166 10   33     81 $d_resource ||= $c->req->url->path;
167 10 50       336 $d_action =~ s/<method>/$c->req->method/e if $d_action;
  0         0  
168 10   33     56 $d_action ||= $c->req->method;
169 10         160 $auth_plugin->authorize( $c, $d_action, $d_resource );
170             }
171 3         10 });
172             }
173             else
174             {
175 1     1   3 $head_route = $routes = $head_authenticated->under->to({ cb => sub { 1 } });
  1         86  
176             }
177 4         498 next;
178             }
179              
180             # ladders don't replace previous ladders
181 45 50 33     180 if (!ref $methods && $methods eq 'ladder')
182             {
183 0 0 0     0 die "constraints not handled in ladders" if $constraints && @$constraints;
184 0         0 $routes = $routes
185             ->under( $pattern )
186             ->over($conditions)
187             ->to($defaults)
188             ->name($name);
189 0         0 next;
190             }
191              
192             # WebSocket?
193 45 100 66     194 my $websocket = 1 if !ref $methods && $methods eq 'websocket';
194 45 100       126 $methods = [] if $websocket;
195              
196             # Create route
197 45         176 my $route = $routes
198             ->route( $pattern, @$constraints )
199             ->over($conditions)
200             ->via($methods)
201             ->to($defaults)
202             ->name($name);
203              
204             # WebSocket
205 45 100       8892 $route->websocket if $websocket;
206             }
207             }
208              
209             1;
210              
211             __END__
212              
213             =pod
214              
215             =encoding UTF-8
216              
217             =head1 NAME
218              
219             Clustericious::RouteBuilder - Route builder for Clustericious applications
220              
221             =head1 VERSION
222              
223             version 1.27
224              
225             =head1 SYNOPSIS
226              
227             package MyApp;
228            
229             use Mojo::Base qw( Clustericious::App );
230            
231             package MyApp::Routes;
232              
233             use Clustericious::RouteBuilder;
234            
235             get '/' => sub { shift->render(text => 'welcome to myapp') };
236              
237             =head1 DESCRIPTION
238              
239             This module provides a simplified interface for creating routes for your
240             L<Clustericious> application. To use it, create a Routes.pm that lives
241             directly under your application's namespace (for example above MyApp's
242             route module is MyApp::Routes). The interface is reminiscent of
243             L<Mojolicious::Lite>, because it was forked from there some time ago.
244              
245             =head1 FUNCTIONS
246              
247             =head2 any
248              
249             Define an HTTP route that matches any HTTP command verb.
250              
251             =head2 get
252              
253             Define an HTTP GET route
254              
255             =head2 head
256              
257             Define an HTTP HEAD route
258              
259             =head2 post
260              
261             Define an HTTP POST route
262              
263             =head2 put
264              
265             Define an HTTP PUT route
266              
267             =head2 del
268              
269             Define an HTTP DELETE route.
270              
271             =head2 websocket
272              
273             Define a Websocket route.
274              
275             =head2 authenticate
276              
277             Require authentication for all subsequent routes.
278              
279             =head2 authorize [ $action ]
280              
281             Require specific authorization for all subsequent routes.
282              
283             =head1 SEE ALSO
284              
285             L<Clustericious>, L<Mojolicious::Lite>
286              
287             =head1 AUTHOR
288              
289             Original author: Brian Duggan
290              
291             Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
292              
293             Contributors:
294              
295             Curt Tilmes
296              
297             Yanick Champoux
298              
299             =head1 COPYRIGHT AND LICENSE
300              
301             This software is copyright (c) 2013 by NASA GSFC.
302              
303             This is free software; you can redistribute it and/or modify it under
304             the same terms as the Perl 5 programming language system itself.
305              
306             =cut