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{/};
51 # We need to turn the full chain name into a path, since two end points
52 # from two different chains could have the same end point name.
53 my $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, @_ );
79 $meta->add_chain_point( $name => [ $attrs, $sub ] );
87 my $caller = ( caller(2) )[3];
90 "The $caller keyword expects a path string or regex as its first argument"
91 unless _STRINGLIKE0($path) || _REGEX($path);
93 die "The $caller keyword expects a sub ref as its final argument"
94 unless _CODELIKE($sub);
98 unless ( delete $p{chain_point} ) {
99 $p{ActionClass} ||= 'REST::ForBrowsers';
102 unless ( $p{PathPart} ) {
105 unless ( exists $p{Chained} ) {
106 unless ( $part =~ s{^/}{} ) {
108 $meta->name()->action_namespace('FakeConfig'), $part;
112 $p{PathPart} = [$part];
118 sub _maybe_add_rest_route {
123 return if $meta->has_method($name);
125 # This could be done by Moose::Exporter, but that would require that the
126 # module has already inherited from Cat::Controller when it calls "use
127 # CatalystX::Routes".
128 unless ( $meta->does_role('CatalystX::Routes::Role::Controller') ) {
131 'CatalystX::Routes::Role::Controller'
135 $meta->add_method( $name => sub { } );
137 $meta->add_route( $name => [ $attrs, $meta->get_method($name) ] );
143 return ( Chained => $_[0] );
147 return ( Args => [ $_[0] ] );
150 sub capture_args ($) {
151 return ( CaptureArgs => [ $_[0] ] );
155 return ( PathPart => [ $_[0] ] );
158 sub action_class_name ($) {
159 return ( ActionClass => [ $_[0] ] );
162 # XXX - this should be added to Params::Util
163 sub _STRINGLIKE0 ($) {
164 return _STRING( $_[0] )
168 && overload::Method( $_[0], q{""} )
174 # This is a nasty hack around some weird back compat code in
175 # Catalyst::Controller->action_namespace
179 return { case_sensitive => 0 };
185 # ABSTRACT: Sugar for declaring RESTful chained action in Catalyst
191 package MyApp::Controller::User;
194 use CatalystX::Routes;
196 BEGIN { extends 'Catalyst::Controller'; }
200 chain_point '_set_user'
209 $c->stash()->{user} = ...;
214 => chained('_set_user')
219 get 'foo' => sub { ... }
224 post 'foo' => \&_post;
227 put '/root' => sub { ... };
229 # /user/plain_old_catalyst
230 sub plain_old_catalyst : Local { ... }
234 This module provides a sugar layer that allows controllers to declare chained
237 Under the hood, all the sugar declarations are turned into Chained subs. All
238 chain end points are declared using one of C<get>, C<get_html>, C<post>,
239 C<put>, or C<del>. These will declare actions using the
240 L<Catalyst::Action::REST::ForBrowsers> action class from the
241 L<Catalyst::Action::REST> distribution.
243 =head1 PUTTING IT ALL TOGETHER
245 This module is merely sugar over Catalyst's built-in L<Chained
246 dispatching|Catalyst::DispatchType::Chained> and L<Catalyst::Action::REST>. It
247 helps to know how those two things work.
249 =head1 SUGAR FUNCTIONS
251 All of these functions will be exported into your controller class when you
252 use C<CatalystX::Routes>.
256 This declares a C<GET> handler.
260 This declares a C<GET> handler for browsers. Use this to generate a standard
261 HTML page for browsers while still being able to generate some sort of RESTful
262 data response for other clients.
264 If a browser makes a C<GET> request and no C<get_html> action has been
265 declared, a C<get> action is used as a fallback. See
266 C<Catalyst::TraitFor::Request::REST::ForBrowsers> for details on how
267 "browser-ness" is determined.
271 This declares a C<POST> handler.
275 This declares a C<PUT> handler.
279 This declares a C<DELETE> handler.
283 This declares an intermediate chain point that should not be exposed as a
288 This function takes a single argument, the previous chain point from which the
293 This declares the number of arguments that this action expects. This should
294 only be used for the end of a chain.
296 =head2 capture_args $number
298 The number of arguments to capture at this point in the chain. This should
299 only be used for the beginning or middle parts of a chain.
301 =head2 path_part $path
303 The path part for this part of the chain. If you are declaring a chain end
304 point with C<get>, etc., then this isn't necessary. By default, the name
305 passed to the initial sugar function will be converted to a path part. See
308 =head2 action_class_name $class
310 Use this to declare an action class. By default, this will be
311 L<Catalyst::Action::REST::ForBrowsers> for end points. For other parts of a
312 chain, it simply won't be set.
314 =head1 Path Generation
316 All of the end point function (C<get>, C<post>, etc.) take a path as the first
317 argument. By default, this will be used as the C<path_part> for the chain. You
318 can override this by explicitly calling C<path_part>, in which case the name
319 is essentially ignored (but still required).
321 Note that it is legitimate to pass the empty string as the name for a chain's
324 If the end point's name does not start with a slash, it will be prefixed with
325 the controller's namespace.
327 If you don't specify a C<chained> value for an end point, then it will use the
328 root URI, C</>, as the root of the chain.
330 By default, no arguments are specified for a chain's end point, meaning it
331 will accept any number of arguments.
335 Please report any bugs or feature requests to
336 C<bug-catalystx-routes@rt.cpan.org>, or through the web interface at
337 L<http://rt.cpan.org>. I will be notified, and then you'll automatically be
338 notified of progress on your bug as I make changes.