X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalystX%2FRoutes.pm;h=0e4acc38a93340a76f846411b9a9672add1f80c9;hb=bd470bcc04e551734d33e136d4824cce0519b1b5;hp=0e9b3956d678d7a9a29d44b5bcddb32f0611532f;hpb=73bef299767c2f6cd268a87c2afb86b69cd9349e;p=catagits%2FCatalystX-Routes.git diff --git a/lib/CatalystX/Routes.pm b/lib/CatalystX/Routes.pm index 0e9b395..0e4acc3 100644 --- a/lib/CatalystX/Routes.pm +++ b/lib/CatalystX/Routes.pm @@ -3,21 +3,15 @@ package CatalystX::Routes; use strict; use warnings; -use CatalystX::Routes::Role::Class; -use CatalystX::Routes::Role::Controller; use Moose::Util qw( apply_all_roles ); use Params::Util qw( _CODELIKE _REGEX _STRING ); use Scalar::Util qw( blessed ); -use Sub::Identify qw( sub_name ); use Moose::Exporter; Moose::Exporter->setup_import_methods( - with_meta => [qw( get get_html post put del chain_point )], - as_is => [qw( chained args capture_args path_part action )], - class_metaroles => { - class => ['CatalystX::Routes::Role::Class'], - }, + with_meta => [qw( get get_html post put del chain_point )], + as_is => [qw( chained args capture_args path_part action_class_name )], ); sub get { @@ -43,7 +37,21 @@ sub del { sub _add_route { my $rest = shift; my $meta = shift; - my ( $name, $attrs, $sub ) = _process_args( $meta, @_ ); + my ( $attrs, $sub ) = _process_args( $meta, @_ ); + + unless ( exists $attrs->{Chained} ) { + $attrs->{Chained} = q{/}; + } + + my $name = $_[0]; + $name =~ s{^/}{}; + + # We need to turn the full chain name into a path, since two end points + # from two different chains could have the same end point name. + $name = ( $attrs->{Chained} eq '/' ? q{} : $attrs->{Chained} ) . q{/} + . $name; + + $name =~ s{/}{|}g; my $meth_base = '__route__' . $name; @@ -64,9 +72,14 @@ sub chain_point { sub _add_chain_point { my $meta = shift; - my ( $name, $attrs, $sub ) = _process_args( $meta, @_ ); + my ( $attrs, $sub ) = _process_args( $meta, @_ ); - $meta->add_chain_point( $name => [ $attrs, $sub ] ); + my $name = $_[0]; + $name =~ s{/}{|}g; + + $meta->add_method( $name => $sub ); + + $meta->name()->config()->{actions}{$name} = $attrs; } sub _process_args { @@ -96,23 +109,14 @@ sub _process_args { unless ( $part =~ s{^/}{} ) { $part = join q{/}, $meta->name()->action_namespace('FakeConfig'), $part; + $part =~ s{^/}{}; } } $p{PathPart} = [$part]; } - unless ( $p{CaptureArgs} || $p{Args} ) { - $p{Args} = [0]; - } - - unless ( exists $p{Chained} ) { - $p{Chained} = q{/}; - } - - ( my $name = $path ) =~ s/(\W)/'X' . sprintf( '%x', ord($1) )/eg; - - return $name, \%p, $sub; + return \%p, $sub; } sub _maybe_add_rest_route { @@ -122,19 +126,9 @@ sub _maybe_add_rest_route { return if $meta->has_method($name); - # This could be done by Moose::Exporter, but that would require that the - # module has already inherited from Cat::Controller when it calls "use - # CatalystX::Routes". - unless ( $meta->does_role('CatalystX::Routes::Role::Controller') ) { - apply_all_roles( - $meta->name(), - 'CatalystX::Routes::Role::Controller' - ); - } - $meta->add_method( $name => sub { } ); - $meta->add_route( $name => [ $attrs, $meta->get_method($name) ] ); + $meta->name()->config()->{actions}{$name} = $attrs; return; } @@ -155,7 +149,7 @@ sub path_part ($) { return ( PathPart => [ $_[0] ] ); } -sub action ($) { +sub action_class_name ($) { return ( ActionClass => [ $_[0] ] ); } @@ -181,3 +175,172 @@ sub _STRINGLIKE0 ($) { } 1; + +# ABSTRACT: Sugar for declaring RESTful chained actions in Catalyst + +__END__ + +=head1 SYNOPSIS + + package MyApp::Controller::User; + + use Moose; + use CatalystX::Routes; + + BEGIN { extends 'Catalyst::Controller'; } + + # /user/:user_id + + chain_point '_set_user' + => chained '/' + => path_part 'user' + => capture_args 1 + => sub { + my $self = shift; + my $c = shift; + my $user_id = shift; + + $c->stash()->{user} = ...; + }; + + # GET /user/:user_Id + get '' + => chained('_set_user') + => args 0 + => sub { ... }; + + # GET /user/foo + get 'foo' => sub { ... }; + + sub _post { ... } + + # POST /user/foo + post 'foo' => \&_post; + + # PUT /root + put '/root' => sub { ... }; + + # /user/plain_old_catalyst + sub plain_old_catalyst : Local { ... } + +=head1 DESCRIPTION + +B: This module is still experimental. It works well, but the APIs may +change without warning. + +This module provides a sugar layer that allows controllers to declare chained +RESTful actions. + +Under the hood, all the sugar declarations are turned into Chained subs. All +chain end points are declared using one of C, C, C, +C, or C. These will declare actions using the +L action class from the +L distribution. + +=head1 PUTTING IT ALL TOGETHER + +This module is merely sugar over Catalyst's built-in L and L. It +helps to know how those two things work. + +=head1 SUGAR FUNCTIONS + +All of these functions will be exported into your controller class when you +use C. + +=head2 get ... + +This declares a C handler. + +=head2 get_html + +This declares a C handler for browsers. Use this to generate a standard +HTML page for browsers while still being able to generate some sort of RESTful +data response for other clients. + +If a browser makes a C request and no C action has been +declared, a C action is used as a fallback. See +C for details on how +"browser-ness" is determined. + +=head2 post ... + +This declares a C handler. + +=head2 put + +This declares a C handler. + +=head2 del + +This declares a C handler. + +=head2 chain_point + +This declares an intermediate chain point that should not be exposed as a +public URI. + +=head2 chained $path + +This function takes a single argument, the previous chain point from which the +action is chained. + +=head2 args $number + +This declares the number of arguments that this action expects. This should +only be used for the end of a chain. + +=head2 capture_args $number + +The number of arguments to capture at this point in the chain. This should +only be used for the beginning or middle parts of a chain. + +=head2 path_part $path + +The path part for this part of the chain. If you are declaring a chain end +point with C, etc., then this isn't necessary. By default, the name +passed to the initial sugar function will be converted to a path part. See +below for details. + +=head2 action_class_name $class + +Use this to declare an action class. By default, this will be +L for end points. For other parts of a +chain, it simply won't be set. + +=head1 PATH GENERATION + +All of the end point function (C, C, etc.) take a path as the first +argument. By default, this will be used as the C for the chain. You +can override this by explicitly calling C, in which case the name +is essentially ignored (but still required). + +Note that it is legitimate to pass the empty string as the name for a chain's +end point. + +If the end point's name does not start with a slash, it will be prefixed with +the controller's namespace. + +If you don't specify a C value for an end point, then it will use the +root URI, C, as the root of the chain. + +By default, no arguments are specified for a chain's end point, meaning it +will accept any number of arguments. + +=head1 CAVEATS + +When adding subroutines for end points to your controller, a name is generated +for each subroutine based on the chained path to the subroutine. Some +template-based views will automatically pick a template based on the +subroutine's name if you don't specify one explicitly. This won't work very +well with the bizarro names that this module generates, so you are strongly +encouraged to specify a template name explicitly. + +=head1 BUGS + +Please report any bugs or feature requests to +C, or through the web interface at +L. I will be notified, and then you'll automatically be +notified of progress on your bug as I make changes. + +=cut