1 package CatalystX::Routes;
6 use Moose::Util qw( apply_all_roles );
7 use Params::Util qw( _CODELIKE _REGEX _STRING );
8 use Scalar::Util qw( blessed );
12 Moose::Exporter->setup_import_methods(
13 with_meta => [qw( get get_html post put del chain_point )],
14 as_is => [qw( chained args capture_args path_part action_class_name )],
18 _add_route( 'GET', @_ );
22 _add_route( 'GET_html', @_ );
26 _add_route( 'POST', @_ );
30 _add_route( 'PUT', @_ );
34 _add_route( 'DELETE', @_ );
40 my ( $attrs, $sub ) = _process_args( $meta, @_ );
42 unless ( exists $attrs->{Chained} ) {
43 $attrs->{Chained} = q{/};
49 # We need to turn the full chain name into a path, since two end points
50 # from two different chains could have the same end point name.
51 $name = ( $attrs->{Chained} eq '/' ? q{} : $attrs->{Chained} ) . q{/}
56 my $meth_base = '__route__' . $name;
58 _maybe_add_rest_route( $meta, $meth_base, $attrs );
60 my $meth_name = $meth_base . q{_} . $rest;
62 $meta->add_method( $meth_name => sub { goto &$sub } );
70 _add_chain_point( $meta, $name, chain_point => 1, @_ );
73 sub _add_chain_point {
75 my ( $attrs, $sub ) = _process_args( $meta, @_ );
80 $meta->add_method( $name => $sub );
82 $meta->name()->config()->{actions}{$name} = $attrs;
90 my $caller = ( caller(2) )[3];
93 "The $caller keyword expects a path string or regex as its first argument"
94 unless _STRINGLIKE0($path) || _REGEX($path);
96 die "The $caller keyword expects a sub ref as its final argument"
97 unless _CODELIKE($sub);
101 unless ( delete $p{chain_point} ) {
102 $p{ActionClass} ||= 'REST::ForBrowsers';
105 unless ( $p{PathPart} ) {
108 unless ( exists $p{Chained} ) {
109 unless ( $part =~ s{^/}{} ) {
111 $meta->name()->action_namespace('FakeConfig'), $part;
116 $p{PathPart} = [$part];
122 sub _maybe_add_rest_route {
127 return if $meta->has_method($name);
129 $meta->add_method( $name => sub { } );
131 $meta->name()->config()->{actions}{$name} = $attrs;
137 return ( Chained => $_[0] );
141 return ( Args => [ $_[0] ] );
144 sub capture_args ($) {
145 return ( CaptureArgs => [ $_[0] ] );
149 return ( PathPart => [ $_[0] ] );
152 sub action_class_name ($) {
153 return ( ActionClass => [ $_[0] ] );
156 # XXX - this should be added to Params::Util
157 sub _STRINGLIKE0 ($) {
158 return _STRING( $_[0] )
162 && overload::Method( $_[0], q{""} )
168 # This is a nasty hack around some weird back compat code in
169 # Catalyst::Controller->action_namespace
173 return { case_sensitive => 0 };
179 # ABSTRACT: Sugar for declaring RESTful chained actions in Catalyst
185 package MyApp::Controller::User;
188 use CatalystX::Routes;
190 BEGIN { extends 'Catalyst::Controller'; }
194 chain_point '_set_user'
203 $c->stash()->{user} = ...;
208 => chained('_set_user')
213 get 'foo' => sub { ... };
218 post 'foo' => \&_post;
221 put '/root' => sub { ... };
223 # /user/plain_old_catalyst
224 sub plain_old_catalyst : Local { ... }
228 B<WARNING>: This module is still experimental. It works well, but the APIs may
229 change without warning.
231 This module provides a sugar layer that allows controllers to declare chained
234 Under the hood, all the sugar declarations are turned into Chained subs. All
235 chain end points are declared using one of C<get>, C<get_html>, C<post>,
236 C<put>, or C<del>. These will declare actions using the
237 L<Catalyst::Action::REST::ForBrowsers> action class from the
238 L<Catalyst::Action::REST> distribution.
240 =head1 PUTTING IT ALL TOGETHER
242 This module is merely sugar over Catalyst's built-in L<Chained
243 dispatching|Catalyst::DispatchType::Chained> and L<Catalyst::Action::REST>. It
244 helps to know how those two things work.
246 =head1 SUGAR FUNCTIONS
248 All of these functions will be exported into your controller class when you
249 use C<CatalystX::Routes>.
253 This declares a C<GET> handler.
257 This declares a C<GET> handler for browsers. Use this to generate a standard
258 HTML page for browsers while still being able to generate some sort of RESTful
259 data response for other clients.
261 If a browser makes a C<GET> request and no C<get_html> action has been
262 declared, a C<get> action is used as a fallback. See
263 C<Catalyst::TraitFor::Request::REST::ForBrowsers> for details on how
264 "browser-ness" is determined.
268 This declares a C<POST> handler.
272 This declares a C<PUT> handler.
276 This declares a C<DELETE> handler.
280 This declares an intermediate chain point that should not be exposed as a
285 This function takes a single argument, the previous chain point from which the
290 This declares the number of arguments that this action expects. This should
291 only be used for the end of a chain.
293 =head2 capture_args $number
295 The number of arguments to capture at this point in the chain. This should
296 only be used for the beginning or middle parts of a chain.
298 =head2 path_part $path
300 The path part for this part of the chain. If you are declaring a chain end
301 point with C<get>, etc., then this isn't necessary. By default, the name
302 passed to the initial sugar function will be converted to a path part. See
305 =head2 action_class_name $class
307 Use this to declare an action class. By default, this will be
308 L<Catalyst::Action::REST::ForBrowsers> for end points. For other parts of a
309 chain, it simply won't be set.
311 =head1 PATH GENERATION
313 All of the end point function (C<get>, C<post>, etc.) take a path as the first
314 argument. By default, this will be used as the C<path_part> for the chain. You
315 can override this by explicitly calling C<path_part>, in which case the name
316 is essentially ignored (but still required).
318 Note that it is legitimate to pass the empty string as the name for a chain's
321 If the end point's name does not start with a slash, it will be prefixed with
322 the controller's namespace.
324 If you don't specify a C<chained> value for an end point, then it will use the
325 root URI, C</>, as the root of the chain.
327 By default, no arguments are specified for a chain's end point, meaning it
328 will accept any number of arguments.
332 When adding subroutines for end points to your controller, a name is generated
333 for each subroutine based on the chained path to the subroutine. Some
334 template-based views will automatically pick a template based on the
335 subroutine's name if you don't specify one explicitly. This won't work very
336 well with the bizarro names that this module generates, so you are strongly
337 encouraged to specify a template name explicitly.
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.