1 package CatalystX::Routes;
6 use CatalystX::Routes::Role::Class;
7 use CatalystX::Routes::Role::Controller;
8 use Moose::Util qw( apply_all_roles );
9 use Params::Util qw( _CODELIKE _REGEX _STRING );
10 use Scalar::Util qw( blessed );
14 Moose::Exporter->setup_import_methods(
15 with_meta => [qw( get get_html post put del chain_point )],
16 as_is => [qw( chained args capture_args path_part action_class_name )],
18 class => ['CatalystX::Routes::Role::Class'],
23 _add_route( 'GET', @_ );
27 _add_route( 'GET_html', @_ );
31 _add_route( 'POST', @_ );
35 _add_route( 'PUT', @_ );
39 _add_route( 'DELETE', @_ );
45 my ( $attrs, $sub ) = _process_args( $meta, @_ );
47 unless ( exists $attrs->{Chained} ) {
48 $attrs->{Chained} = q{/};
54 # We need to turn the full chain name into a path, since two end points
55 # from two different chains could have the same end point name.
56 $name = ( $attrs->{Chained} eq '/' ? q{} : $attrs->{Chained} ) . q{/}
61 my $meth_base = '__route__' . $name;
63 _maybe_add_rest_route( $meta, $meth_base, $attrs );
65 my $meth_name = $meth_base . q{_} . $rest;
67 $meta->add_method( $meth_name => sub { goto &$sub } );
75 _add_chain_point( $meta, $name, chain_point => 1, @_ );
78 sub _add_chain_point {
80 my ( $attrs, $sub ) = _process_args( $meta, @_ );
85 $meta->add_chain_point( $name => [ $attrs, $sub ] );
93 my $caller = ( caller(2) )[3];
96 "The $caller keyword expects a path string or regex as its first argument"
97 unless _STRINGLIKE0($path) || _REGEX($path);
99 die "The $caller keyword expects a sub ref as its final argument"
100 unless _CODELIKE($sub);
104 unless ( delete $p{chain_point} ) {
105 $p{ActionClass} ||= 'REST::ForBrowsers';
108 unless ( $p{PathPart} ) {
111 unless ( exists $p{Chained} ) {
112 unless ( $part =~ s{^/}{} ) {
114 $meta->name()->action_namespace('FakeConfig'), $part;
119 $p{PathPart} = [$part];
125 sub _maybe_add_rest_route {
130 return if $meta->has_method($name);
132 # This could be done by Moose::Exporter, but that would require that the
133 # module has already inherited from Cat::Controller when it calls "use
134 # CatalystX::Routes".
135 unless ( $meta->does_role('CatalystX::Routes::Role::Controller') ) {
138 'CatalystX::Routes::Role::Controller'
142 $meta->add_method( $name => sub { } );
144 $meta->add_route( $name => [ $attrs, $meta->get_method($name) ] );
150 return ( Chained => $_[0] );
154 return ( Args => [ $_[0] ] );
157 sub capture_args ($) {
158 return ( CaptureArgs => [ $_[0] ] );
162 return ( PathPart => [ $_[0] ] );
165 sub action_class_name ($) {
166 return ( ActionClass => [ $_[0] ] );
169 # XXX - this should be added to Params::Util
170 sub _STRINGLIKE0 ($) {
171 return _STRING( $_[0] )
175 && overload::Method( $_[0], q{""} )
181 # This is a nasty hack around some weird back compat code in
182 # Catalyst::Controller->action_namespace
186 return { case_sensitive => 0 };
192 # ABSTRACT: Sugar for declaring RESTful chained action in Catalyst
198 package MyApp::Controller::User;
201 use CatalystX::Routes;
203 BEGIN { extends 'Catalyst::Controller'; }
207 chain_point '_set_user'
216 $c->stash()->{user} = ...;
221 => chained('_set_user')
226 get 'foo' => sub { ... };
231 post 'foo' => \&_post;
234 put '/root' => sub { ... };
236 # /user/plain_old_catalyst
237 sub plain_old_catalyst : Local { ... }
241 This module provides a sugar layer that allows controllers to declare chained
244 Under the hood, all the sugar declarations are turned into Chained subs. All
245 chain end points are declared using one of C<get>, C<get_html>, C<post>,
246 C<put>, or C<del>. These will declare actions using the
247 L<Catalyst::Action::REST::ForBrowsers> action class from the
248 L<Catalyst::Action::REST> distribution.
250 =head1 PUTTING IT ALL TOGETHER
252 This module is merely sugar over Catalyst's built-in L<Chained
253 dispatching|Catalyst::DispatchType::Chained> and L<Catalyst::Action::REST>. It
254 helps to know how those two things work.
256 =head1 SUGAR FUNCTIONS
258 All of these functions will be exported into your controller class when you
259 use C<CatalystX::Routes>.
263 This declares a C<GET> handler.
267 This declares a C<GET> handler for browsers. Use this to generate a standard
268 HTML page for browsers while still being able to generate some sort of RESTful
269 data response for other clients.
271 If a browser makes a C<GET> request and no C<get_html> action has been
272 declared, a C<get> action is used as a fallback. See
273 C<Catalyst::TraitFor::Request::REST::ForBrowsers> for details on how
274 "browser-ness" is determined.
278 This declares a C<POST> handler.
282 This declares a C<PUT> handler.
286 This declares a C<DELETE> handler.
290 This declares an intermediate chain point that should not be exposed as a
295 This function takes a single argument, the previous chain point from which the
300 This declares the number of arguments that this action expects. This should
301 only be used for the end of a chain.
303 =head2 capture_args $number
305 The number of arguments to capture at this point in the chain. This should
306 only be used for the beginning or middle parts of a chain.
308 =head2 path_part $path
310 The path part for this part of the chain. If you are declaring a chain end
311 point with C<get>, etc., then this isn't necessary. By default, the name
312 passed to the initial sugar function will be converted to a path part. See
315 =head2 action_class_name $class
317 Use this to declare an action class. By default, this will be
318 L<Catalyst::Action::REST::ForBrowsers> for end points. For other parts of a
319 chain, it simply won't be set.
321 =head1 PATH GENERATION
323 All of the end point function (C<get>, C<post>, etc.) take a path as the first
324 argument. By default, this will be used as the C<path_part> for the chain. You
325 can override this by explicitly calling C<path_part>, in which case the name
326 is essentially ignored (but still required).
328 Note that it is legitimate to pass the empty string as the name for a chain's
331 If the end point's name does not start with a slash, it will be prefixed with
332 the controller's namespace.
334 If you don't specify a C<chained> value for an end point, then it will use the
335 root URI, C</>, as the root of the chain.
337 By default, no arguments are specified for a chain's end point, meaning it
338 will accept any number of arguments.
342 When adding subroutines for end points to your controller, a name is generated
343 for each subroutine based on the chained path to the subroutine. Some
344 template-based views will automatically pick a template based on the
345 subroutine's name if you don't specify one explicitly. This won't work very
346 well with the bizarro names that this module generates, so you are strongly
347 encouraged to specify a template name explicitly.
351 Please report any bugs or feature requests to
352 C<bug-catalystx-routes@rt.cpan.org>, or through the web interface at
353 L<http://rt.cpan.org>. I will be notified, and then you'll automatically be
354 notified of progress on your bug as I make changes.