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;
118 $p{PathPart} = [$part];
124 sub _maybe_add_rest_route {
129 return if $meta->has_method($name);
131 # This could be done by Moose::Exporter, but that would require that the
132 # module has already inherited from Cat::Controller when it calls "use
133 # CatalystX::Routes".
134 unless ( $meta->does_role('CatalystX::Routes::Role::Controller') ) {
137 'CatalystX::Routes::Role::Controller'
141 $meta->add_method( $name => sub { } );
143 $meta->add_route( $name => [ $attrs, $meta->get_method($name) ] );
149 return ( Chained => $_[0] );
153 return ( Args => [ $_[0] ] );
156 sub capture_args ($) {
157 return ( CaptureArgs => [ $_[0] ] );
161 return ( PathPart => [ $_[0] ] );
164 sub action_class_name ($) {
165 return ( ActionClass => [ $_[0] ] );
168 # XXX - this should be added to Params::Util
169 sub _STRINGLIKE0 ($) {
170 return _STRING( $_[0] )
174 && overload::Method( $_[0], q{""} )
180 # This is a nasty hack around some weird back compat code in
181 # Catalyst::Controller->action_namespace
185 return { case_sensitive => 0 };
191 # ABSTRACT: Sugar for declaring RESTful chained action in Catalyst
197 package MyApp::Controller::User;
200 use CatalystX::Routes;
202 BEGIN { extends 'Catalyst::Controller'; }
206 chain_point '_set_user'
215 $c->stash()->{user} = ...;
220 => chained('_set_user')
225 get 'foo' => sub { ... };
230 post 'foo' => \&_post;
233 put '/root' => sub { ... };
235 # /user/plain_old_catalyst
236 sub plain_old_catalyst : Local { ... }
240 This module provides a sugar layer that allows controllers to declare chained
243 Under the hood, all the sugar declarations are turned into Chained subs. All
244 chain end points are declared using one of C<get>, C<get_html>, C<post>,
245 C<put>, or C<del>. These will declare actions using the
246 L<Catalyst::Action::REST::ForBrowsers> action class from the
247 L<Catalyst::Action::REST> distribution.
249 =head1 PUTTING IT ALL TOGETHER
251 This module is merely sugar over Catalyst's built-in L<Chained
252 dispatching|Catalyst::DispatchType::Chained> and L<Catalyst::Action::REST>. It
253 helps to know how those two things work.
255 =head1 SUGAR FUNCTIONS
257 All of these functions will be exported into your controller class when you
258 use C<CatalystX::Routes>.
262 This declares a C<GET> handler.
266 This declares a C<GET> handler for browsers. Use this to generate a standard
267 HTML page for browsers while still being able to generate some sort of RESTful
268 data response for other clients.
270 If a browser makes a C<GET> request and no C<get_html> action has been
271 declared, a C<get> action is used as a fallback. See
272 C<Catalyst::TraitFor::Request::REST::ForBrowsers> for details on how
273 "browser-ness" is determined.
277 This declares a C<POST> handler.
281 This declares a C<PUT> handler.
285 This declares a C<DELETE> handler.
289 This declares an intermediate chain point that should not be exposed as a
294 This function takes a single argument, the previous chain point from which the
299 This declares the number of arguments that this action expects. This should
300 only be used for the end of a chain.
302 =head2 capture_args $number
304 The number of arguments to capture at this point in the chain. This should
305 only be used for the beginning or middle parts of a chain.
307 =head2 path_part $path
309 The path part for this part of the chain. If you are declaring a chain end
310 point with C<get>, etc., then this isn't necessary. By default, the name
311 passed to the initial sugar function will be converted to a path part. See
314 =head2 action_class_name $class
316 Use this to declare an action class. By default, this will be
317 L<Catalyst::Action::REST::ForBrowsers> for end points. For other parts of a
318 chain, it simply won't be set.
320 =head1 Path Generation
322 All of the end point function (C<get>, C<post>, etc.) take a path as the first
323 argument. By default, this will be used as the C<path_part> for the chain. You
324 can override this by explicitly calling C<path_part>, in which case the name
325 is essentially ignored (but still required).
327 Note that it is legitimate to pass the empty string as the name for a chain's
330 If the end point's name does not start with a slash, it will be prefixed with
331 the controller's namespace.
333 If you don't specify a C<chained> value for an end point, then it will use the
334 root URI, C</>, as the root of the chain.
336 By default, no arguments are specified for a chain's end point, meaning it
337 will accept any number of arguments.
341 Please report any bugs or feature requests to
342 C<bug-catalystx-routes@rt.cpan.org>, or through the web interface at
343 L<http://rt.cpan.org>. I will be notified, and then you'll automatically be
344 notified of progress on your bug as I make changes.