use
5.010;
our
$VERSION
=
'1.29'
;
my
%routes
;
sub
import
{
my
(
$class
,
$app_class
) =
@_
;
my
$caller
=
caller
;
unless
(
$app_class
)
{
if
(
$caller
->isa(
"Clustericious::App"
))
{
$app_class
=
$caller
;
}
else
{
$app_class
=
$caller
;
$app_class
=~ s/::Routes$// or
die
"could not guess app class : "
;
}
}
my
@routes
;
$routes
{
$app_class
} = \
@routes
;
my
$route_sub
=
sub
{
my
(
$methods
,
@args
) =
@_
;
my
(
$cb
,
$constraints
,
$defaults
,
$name
,
$pattern
);
my
$conditions
= [];
my
$condition
;
while
(
my
$arg
=
shift
@args
)
{
if
(
$condition
)
{
push
@$conditions
,
$condition
=>
$arg
;
$condition
=
undef
;
}
elsif
(!
ref
$arg
&& !
$pattern
)
{
$pattern
=
$arg
;
}
elsif
(!
ref
$arg
&&
@args
)
{
$condition
=
$arg
;
}
elsif
(!
ref
$arg
)
{
$name
=
$arg
;
}
elsif
(
ref
$arg
eq
'CODE'
)
{
$cb
=
$arg
;
}
elsif
(
ref
$arg
eq
'ARRAY'
)
{
$constraints
=
$arg
;
}
elsif
(
ref
$arg
eq
'HASH'
)
{
$defaults
=
$arg
;
}
}
$cb
||=
sub
{1};
$constraints
||= [];
$defaults
||= {};
$defaults
= {
%$defaults
,
cb
=>
$cb
};
$name
||=
''
;
push
@routes
, {
name
=>
$name
,
pattern
=>
$pattern
,
constraints
=>
$constraints
,
conditions
=>
$conditions
,
defaults
=>
$defaults
,
methods
=>
$methods
};
};
monkey_patch
$app_class
,
startup_route_builder
=> \
&_startup_route_builder
;
monkey_patch
$caller
,
any
=>
sub
{
$route_sub
->(
ref
$_
[0] ?
shift
: [],
@_
) };
monkey_patch
$caller
,
$_
=>
sub
{
unshift
@_
,
'delete'
;
goto
$route_sub
}
for
qw( Delete del )
;
foreach
my
$method
(
qw( get head ladder post put websocket authenticate authorize )
)
{
monkey_patch
$caller
,
$method
=>
sub
{
unshift
@_
,
$method
;
goto
$route_sub
};
}
}
sub
_startup_route_builder {
my
(
$app
,
$auth_plugin
) =
@_
;
my
$stashed
=
$routes
{
ref
$app
} //
do
{ WARN
"no routes stashed for $app"
; [] };
my
@stashed
=
@$stashed
;
my
$routes
=
$app
->routes;
my
$head_route
=
$app
->routes;
my
$head_authenticated
=
$head_route
;
for
my
$spec
(
@stashed
)
{
my
(
$name
,
$pattern
,
$constraints
,
$conditions
,
$defaults
,
$methods
) =
@$spec
{
qw/name pattern constraints conditions defaults methods/
};
if
(!
ref
$methods
&&
$methods
eq
'authenticate'
)
{
my
$realm
=
$pattern
||
ref
$app
;
my
$cb
=
defined
$auth_plugin
?
sub
{
$auth_plugin
->authenticate(
shift
,
$realm
) } :
sub
{ 1 };
$head_route
=
$head_authenticated
=
$routes
=
$app
->routes->under->to( {
cb
=>
$cb
} )->name(
"authenticated"
);
next
;
}
if
(!
ref
$methods
&&
$methods
eq
'authorize'
)
{
die
"put authenticate before authorize"
unless
$head_authenticated
;
my
$action
=
$pattern
;
my
$resource
=
$name
;
if
(
$auth_plugin
)
{
$head_route
=
$routes
=
$head_authenticated
->under->to( {
cb
=>
sub
{
my
(
$c
) =
@_
;
my
(
$d_resource
,
$d_action
) = (
$resource
,
$action
);
$d_resource
=~ s/<path>/
$c
->req->url->path/e
if
$d_resource
;
$d_resource
||=
$c
->req->url->path;
$d_action
=~ s/<method>/
$c
->req->method/e
if
$d_action
;
$d_action
||=
$c
->req->method;
$auth_plugin
->authorize(
$c
,
$d_action
,
$d_resource
);
}
});
}
else
{
$head_route
=
$routes
=
$head_authenticated
->under->to({
cb
=>
sub
{ 1 } });
}
next
;
}
if
(!
ref
$methods
&&
$methods
eq
'ladder'
)
{
die
"constraints not handled in ladders"
if
$constraints
&&
@$constraints
;
$routes
=
$routes
->under(
$pattern
)
->over(
$conditions
)
->to(
$defaults
)
->name(
$name
);
next
;
}
my
$websocket
= 1
if
!
ref
$methods
&&
$methods
eq
'websocket'
;
$methods
= []
if
$websocket
;
my
$route
=
$routes
->route(
$pattern
,
@$constraints
)
->over(
$conditions
)
->via(
$methods
)
->to(
$defaults
)
->name(
$name
);
$route
->websocket
if
$websocket
;
}
}
1;