$ in character class is still special
[CGI-Application-Plugin-RunmodeDeclare.git] / lib / CGI / Application / Plugin / RunmodeDeclare.pm
blob6533511b1e9722c66a144a1472e2ca5bda38377c
1 package CGI::Application::Plugin::RunmodeDeclare;
3 use warnings;
4 use strict;
6 our $VERSION = '0.06';
8 =head1 NAME
10 CGI::Application::Plugin::RunmodeDeclare - Declare runmodes with keywords
12 =head1 VERSION
14 Version 0.06
16 =cut
18 use base 'Devel::Declare::MethodInstaller::Simple';
19 use Carp qw(croak);
21 sub import {
22 my $class = shift;
23 my $caller = caller;
25 my %remap = (
26 runmode => runmode =>
27 startmode => startmode =>
28 errormode => errormode =>
29 invocant => '$self' =>
30 into => $caller,
31 @_ );
33 $class->install_methodhandler(
34 into => $remap{into},
35 name => $remap{runmode},
36 pre_install => \&_setup_runmode,
37 invocant => $remap{invocant},
39 $class->install_methodhandler(
40 into => $remap{into},
41 name => $remap{startmode},
42 pre_install => \&_setup_startmode,
43 invocant => $remap{invocant},
45 $class->install_methodhandler(
46 into => $remap{into},
47 name => $remap{errormode},
48 pre_install => \&_setup_errormode,
49 invocant => $remap{invocant},
54 my %REGISTRY;
55 # per-macro setup
56 sub _split {
57 my $n = shift; my ($p,$l) = $n =~ /^(.*?)(?:::(\w*))?$/; return ($p, $l);
59 sub _setup_runmode {
60 my ($fullname, $code) = @_;
61 my ($pkg, $name) = _split($fullname);
62 $pkg->add_callback( init => sub { $_[0]->run_modes([ $name ]) } );
64 sub _setup_startmode {
65 my ($fullname, $code) = @_;
66 no strict 'refs'; no warnings 'uninitialized';
67 my ($pkg, $name) = _split($fullname);
68 # compile time check
69 croak "start mode redefined (from $REGISTRY{$pkg}{start_mode_installed})" if $REGISTRY{$pkg}{start_mode_installed};
70 $pkg->add_callback(
71 init => sub {
72 # run time check
73 return if exists $_[0]->{__START_MODE_SET_BY_RUNMODEDECLARE};
74 $_[0]->run_modes( [$name] );
75 $_[0]->start_mode($name);
76 $_[0]->{__START_MODE_SET_BY_RUNMODEDECLARE} = 1;
79 $REGISTRY{$pkg}{start_mode_installed} = $fullname;
81 sub _setup_errormode {
82 my ($fullname, $code) = @_;
83 no strict 'refs'; no warnings 'uninitialized';
84 my ($pkg, $name) = _split($fullname);
85 croak "error mode redefined (from $REGISTRY{$pkg}{error_mode_installed})" if $REGISTRY{$pkg}{error_mode_installed};
86 $pkg->add_callback(
87 init => sub {
88 return if exists $_[0]->{__ERROR_MODE_SET_BY_RUNMODEDECLARE};
89 $_[0]->error_mode($name);
90 $_[0]->{__ERROR_MODE_SET_BY_RUNMODEDECLARE} = 1;
93 $REGISTRY{$pkg}{error_mode_installed} = $fullname;
96 =begin pod-coverage
98 =over 4
100 =item strip_name - we hook into this to install cgiapp callbacks
102 =item parse_proto - proto parser
104 =item inject_parsed_proto - turn it into code
106 =back
108 =end pod-coverage
110 =cut
112 sub strip_name {
113 my $ctx = shift;
115 my $name = $ctx->SUPER::strip_name;
116 $ctx->{pre_install}->($ctx->get_curstash_name . '::' . $name);
118 return $name;
121 sub parse_proto {
122 my $self = shift;
123 my ($proto) = @_;
124 $proto ||= '';
126 my $invocant = $self->{invocant};
127 $invocant = $1 if $proto =~ s{^(\$\w+):\s*}{};
129 my @args =
130 map { m{^ ([\$@%])(\w+) }x ? [$1, $2] : () }
131 split /\s*,\s*/,
132 $proto
135 return (
136 $invocant,
137 $proto,
138 @args,
142 # Turn the parsed signature into Perl code
143 sub inject_parsed_proto {
144 my $self = shift;
145 my ($invocant, $proto, @args) = @_;
147 my @code;
148 push @code, "my $invocant = shift;";
149 push @code, "my ($proto) = \@_;" if defined $proto and length $proto;
151 for my $sig (@args) {
152 my ($sigil, $name) = @$sig;
153 push @code, _default_for($sigil,$name,$invocant);
154 push @code, _default_for($sigil,$name,"${invocant}->query");
157 return join ' ', @code;
160 sub _default_for
162 my $sigil = shift;
163 my $name = shift;
164 my $invocant = shift;
166 return
167 "${sigil}${name} = ${invocant}->param('${name}') unless "
168 . ( $sigil eq '$' ? 'defined' : '' )
169 . " ${sigil}${name}; ";
173 1; # End of CGI::Application::Plugin::RunmodeDeclare
175 __END__
177 =head1 SYNOPSIS
179 package My::CgiApp;
181 use base 'CGI::Application';
182 use CGI::Application::Plugin::RunmodeDeclare;
184 startmode hello { "Hello!" }
186 runmode world($name) {
187 return $self->hello
188 . ', '
189 . $name || "World!";
192 errormode oops($c: $exception) {
193 return "Something went wrong at "
194 . $c->get_current_runmode
195 . ". Exception: $exception";
198 =head1 DESCRIPTION
200 This module allows you to declare run modes with a simple keyword. It provides
201 the same features as L<Method::Signatures::Simple>.
203 It respects inheritance: run modes defined in the superclass are also available
204 in the subclass.
206 Beyond automatically registering the run mode, and providing C<$self>, it also
207 optionally pulls named parameters from C<< $self->query->param >> or
208 C<< $self->param >>.
210 =over 4
212 =item * Basic example
214 runmode foo { $self->bar }
216 This declares the run mode "foo". Notice how C<$self> is ready for use.
218 =item * Rename invocant
220 runmode bar ($c:) { $c->baz }
222 Same as above, only use C<$c> instead of C<$self>.
224 use CGI::Application::Plugin::RunmodeDeclare invocant => '$c';
225 runmode baz { $c->quux }
227 Same as above, but every runmode gets C<$c> by default. You can still say C<runmode ($self:)>
228 to rename the invocant.
230 =item * With a parameter list
232 runmode baz ( $id, $name ) {
233 return $self->wibble("I received $id and $name from a form submission
234 or a method invocation.");
237 Here, we specify that the method expects two parameters, C<$id> and C<$name>.
238 Values can be supplied through a method call (e.g. C<< $self->baz(1, "me") >>),
239 or from the cgiapp object (e.g. C<< $self->param( id => 42 ) >>), or from the
240 query object (e.g. from C</script?id=42;name=me>).
242 =item * Code attributes
244 runmode secret :Auth { ... }
246 Code attributes are supported as well.
248 =item * Combining with other ways to set run modes
250 This all works:
252 sub setup {
253 my $self = shift;
254 $self->run_modes([ qw/ foo / ]);
257 sub foo {
258 my $self = shift;
259 return $self->other;
262 runmode bar {
263 return $self->other;
266 sub other : Runmode {
267 my $self = shift;
268 return $self->param('other');
271 So you can still use the classic way of setting up run modes, and you can
272 still use L<CGI::Application::Plugin::AutoRunmode>, *and* you can mix and match.
274 =back
276 =head1 EXPORT
278 =over 4
280 =item * errormode
282 Define the run mode that serves as C<< $self->error_mode >>. You can only declare one
283 C<errormode> per package.
285 =item * startmode
287 Define the run mode that serves as C<< $self->start_mode >>. You can only declare one
288 C<startmode> per package.
290 =item * runmode
292 Define run mode.
294 =back
296 =head1 AUTHOR
298 Rhesa Rozendaal, C<< <rhesa at cpan.org> >>
300 =head1 DIAGNOSTICS
302 =over 4
304 =item * error mode redefined (from %s) at %s line %s
306 You tried to install another errormode. Placeholders are filled with
308 * fully qualified name of existing errormode
309 * file name
310 * line number
312 =item * start mode redefined (from %s) at %s line %s
314 You tried to install another startmode. Placeholders are filled with
316 * fully qualified name of existing startmode
317 * file name
318 * line number
320 =back
322 =head1 BUGS
324 Please report any bugs or feature requests to
325 C<bug-cgi-application-plugin-runmodedeclare at rt.cpan.org>, or through the web
326 interface at
327 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Application-Plugin-RunmodeDeclare>.
328 I will be notified, and then you'll automatically be notified of progress on
329 your bug as I make changes.
334 =head1 SUPPORT
336 You can find documentation for this module with the perldoc command.
338 perldoc CGI::Application::Plugin::RunmodeDeclare
341 You can also look for information at:
343 =over 4
345 =item * RT: CPAN's request tracker
347 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=CGI-Application-Plugin-RunmodeDeclare>
349 =item * AnnoCPAN: Annotated CPAN documentation
351 L<http://annocpan.org/dist/CGI-Application-Plugin-RunmodeDeclare>
353 =item * CPAN Ratings
355 L<http://cpanratings.perl.org/d/CGI-Application-Plugin-RunmodeDeclare>
357 =item * Search CPAN
359 L<http://search.cpan.org/dist/CGI-Application-Plugin-RunmodeDeclare>
361 =back
364 =head1 ACKNOWLEDGEMENTS
366 Matt S. Trout for L<Devel::Declare>, and Michael G. Schwern for providing
367 the inspiration with L<Method::Signatures>.
369 =head1 COPYRIGHT & LICENSE
371 Copyright 2008 Rhesa Rozendaal, all rights reserved.
373 This program is free software; you can redistribute it and/or modify it
374 under the same terms as Perl itself.
377 =cut