X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=lib%2FMaypole.pm;h=71e01615ed859d55a662fccb0dbb75cfce3c69ff;hb=ad24ffc85f25a9b73a28cd326c3b3caea234adbf;hp=908662f844431fdfa6ca4dc2664abb672f9a8b86;hpb=808f88dcc99bd004c98cbefb759da90512da58eb;p=maypole.git diff --git a/lib/Maypole.pm b/lib/Maypole.pm index 908662f..71e0161 100644 --- a/lib/Maypole.pm +++ b/lib/Maypole.pm @@ -6,8 +6,9 @@ use warnings; use Maypole::Config; use Maypole::Constants; use Maypole::Headers; +use URI(); -our $VERSION = '2.10'; +our $VERSION = '2.11'; # proposed privacy conventions: # - no leading underscore - public to custom application code and plugins @@ -15,18 +16,198 @@ our $VERSION = '2.10'; # including plugins # - double leading underscore - private to the current package +=head1 NAME + +Maypole - MVC web application framework + +=head1 SYNOPSIS + +See L. + +=head1 DESCRIPTION + +This documents the Maypole request object. See the L, for a +detailed guide to using Maypole. + +Maypole is a Perl web application framework similar to Java's struts. It is +essentially completely abstracted, and so doesn't know anything about +how to talk to the outside world. + +To use it, you need to create a driver package which represents your entire +application. This is the C package used as an example in the manual. + +This needs to first use L which will make your package +inherit from the appropriate platform driver such as C or +C. Then, the driver calls C. This sets up the model classes and +configures your application. The default model class for Maypole uses +L to map a database to classes, but this can be changed by altering +configuration (B calling setup.) + + +=head1 DOCUMENTATION AND SUPPORT + +Note that some details in some of these resources may be out of date. + +=over 4 + +=item The Maypole Manual + +The primary documentation is the Maypole manual. This lives in the +C pod documents included with the distribution. + +=item Embedded POD + +Individual packages within the distribution contain (more or less) detailed +reference documentation for their API. + +=item Mailing lists + +There are two mailing lists - maypole-devel and maypole-users - see +http://maypole.perl.org/?MailingList + +=item The Maypole Wiki + +The Maypole wiki provides a useful store of extra documentation - +http://maypole.perl.org + +In particular, there's a FAQ (http://maypole.perl.org/?FAQ) and a cookbook +(http://maypole.perl.org/?Cookbook). Again, certain information on these pages +may be out of date. + +=item Web applications with Maypole + +A tutorial written by Simon Cozens for YAPC::EU 2005 - +http://www.droogs.org/perl/maypole/maypole-tutorial.pdf [228KB]. + +=item A Database-Driven Web Application in 18 Lines of Code + +By Paul Barry, published in Linux Journal, March 2005. + +http://www.linuxjournal.com/article/7937 + +"From zero to Web-based database application in eight easy steps". + +Maypole won a 2005 Linux Journal Editor's Choice Award +(http://www.linuxjournal.com/article/8293) after featuring in this article. + +=item Build Web apps with Maypole + +By Simon Cozens, on IBM's DeveloperWorks website, May 2004. + +http://www-128.ibm.com/developerworks/linux/library/l-maypole/ + +=item Rapid Web Application Deployment with Maypole + +By Simon Cozens, on O'Reilly's Perl website, April 2004. + +http://www.perl.com/pub/a/2004/04/15/maypole.html + +=item Authentication + +Some notes written by Simon Cozens. A little bit out of date, but still +very useful: http://www.droogs.org/perl/maypole/authentication.html + +=item CheatSheet + +There's a refcard for the Maypole (and Class::DBI) APIs on the wiki - +http://maypole.perl.org/?CheatSheet. Probably a little out of date now - it's a +wiki, so feel free to fix any errors! + +=item Plugins and add-ons + +There are a large and growing number of plugins and other add-on modules +available on CPAN - http://search.cpan.org/search?query=maypole&mode=module + +=item del.icio.us + +You can find a range of useful Maypole links, particularly to several thoughtful +blog entries, starting here: http://del.icio.us/search/?all=maypole + +=item CPAN ratings + +There are a couple of short reviews here: +http://cpanratings.perl.org/dist/Maypole + +=back + +=head1 DEMOS + +A couple of demos are available, sometimes with source code and configs. + +=over 4 + +=item http://maypole.perl.org/beerdb/ + +The standard BeerDB example, using the TT factory templates supplied in the +distribution. + +=item beerdb.riverside-cms.co.uk + +The standard BeerDB example, running on Mason, using the factory templates +supplied in the L distribution. + +=item beerfb.riverside-cms.co.uk + +A demo of L. This site is running on the set of Mason +templates included in the L distribution. See the +synopsis of L for an example driver + +=back + +=cut + __PACKAGE__->mk_classdata($_) for qw( config init_done view_object ); + __PACKAGE__->mk_accessors( qw( params query objects model_class template_args output path args action template error document_encoding content_type table - headers_in headers_out ) + headers_in headers_out stash session) ); + __PACKAGE__->config( Maypole::Config->new() ); + __PACKAGE__->init_done(0); -sub debug { 0 } +=head1 CLASS METHODS + +=over 4 + +=item config + +Returns the L object + +=item setup + + My::App->setup($data_source, $user, $password, \%attr); + +Initialise the Maypole application and plugins and model classes - see +L. + +If your model is based on L, the C<\%attr> hashref can +contain options that are passed directly to L, to control +how the model hierarchy is constructed. -sub setup +Your application should call this B setting up configuration data via +L<"config">. + +=cut + +sub setup +{ + my $class = shift; + + $class->setup_model(@_); +} + +=item setup_model + +Called by C. This method builds the Maypole model hierarchy. + +A likely target for over-riding, if you need to build a customised model. + +=cut + +sub setup_model { my $calling_class = shift; @@ -47,9 +228,24 @@ sub setup unshift @{ $subclass . "::ISA" }, $config->model; $config->model->adopt($subclass) if $config->model->can("adopt"); + + # TODO: I think we should also load these classes, in case there is any + # custom code. It would save the developer from needing to put + # lots of use MyApp::SomeTable statements in the driver, and should + # help eliminate some of those annoying silent errors if there's a + # syntax error. } } +=item init + +Loads the view class and instantiates the view object. + +You should not call this directly, but you may wish to override this to add +application-specific initialisation - see L. + +=cut + sub init { my $class = shift; @@ -61,9 +257,14 @@ sub init || $config->display_tables( $class->config->tables ); $class->view_object( $class->config->view->new ); $class->init_done(1); - } +=item new + +Constructs a very minimal new Maypole request object. + +=cut + sub new { my ($class) = @_; @@ -76,10 +277,53 @@ sub new return $self; } +=item view_object + +Get/set the Maypole::View object + +=item debug + + sub My::App::debug {1} + +Returns the debugging flag. Override this in your application class to +enable/disable debugging. + +You can also set the C flag via L. + +=cut + +sub debug { 0 } + +=item get_template_root + +Implementation-specific path to template root. + +You should only need to define this method if you are writing a new Maypole +backend. Otherwise, see L + +=cut + +sub get_template_root {'.'} + +=back + +=head1 INSTANCE METHODS + +=head2 Workflow + +=over 4 + +=item handler + +This method sets up the class if it's not done yet, sets some defaults and +leaves the dirty work to C. + +=cut + # handler() has a method attribute so that mod_perl will invoke # BeerDB->handler() as a method rather than a plain function # BeerDB::handler() and so this inherited implementation will be -# found. See e.g. "Practical mod_perl" by Bekman & Cholet for +# found. See e.g. "Practical mod_perl" by Bekman & Cholet for # more information sub handler : method { @@ -95,7 +339,13 @@ sub handler : method $self->get_request($req); $self->parse_location; - my $status = $self->handler_guts; + # hook useful for declining static requests e.g. images + my $status = $self->start_request_hook; + return $status unless $status == Maypole::Constants::OK(); + + $self->session($self->get_session); + + $status = $self->handler_guts; # moving this here causes unit test failures - need to check why # before committing the move @@ -103,11 +353,21 @@ sub handler : method return $status unless $status == OK; + # TODO: require send_output to return a status code $self->send_output; return $status; } +=item handler_guts + +This is the main request handling method and calls various methods to handle the +request/response and defines the workflow within Maypole. + +B. + +=cut + # The root of all evil sub handler_guts { @@ -115,13 +375,12 @@ sub handler_guts $self->__load_model; - my $applicable = __to_boolean( $self->is_applicable ); + my $applicable = $self->is_model_applicable; $self->__setup_plain_template unless $applicable; - - # We authenticate every request, needed for proper session management + my $status; - + eval { $status = $self->call_authenticate }; if ( my $error = $@ ) @@ -171,6 +430,12 @@ sub handler_guts return $self->__call_process_view; } +sub __load_model +{ + my ($self) = @_; + $self->model_class( $self->config->model->class_of($self, $self->table) ); +} + # is_applicable() returned false, so set up a plain template. Model processing # will be skipped, but need to remove the model anyway so the template can't # access it. @@ -213,48 +478,139 @@ sub __call_process_view return $status; } -sub __load_model +=item get_request + +You should only need to define this method if you are writing a new +Maypole backend. It should return something that looks like an Apache +or CGI request object, it defaults to blank. + +=cut + +sub get_request { } + +=item parse_location + +Turns the backend request (e.g. Apache::MVC, Maypole, CGI) into a Maypole +request. It does this by setting the C, and invoking C and +C. + +You should only need to define this method if you are writing a new Maypole +backend. + +=cut + +sub parse_location { - my ($self) = @_; - $self->model_class( $self->config->model->class_of($self, $self->table) ); + die "parse_location is a virtual method. Do not use Maypole directly; " . + "use Apache::MVC or similar"; } -# is_applicable() should return true or false, not OK or DECLINED, because -# the return value is never used as the return value from handler(). There's -# probably a lot of code out there supplying the return codes though, so -# instead of changing is_applicable() to return 0 or 1, the return value is -# passed through __to_boolean. I think it helps handler_guts() if we don't -# have multiple sets of return codes being checked for different things -drb. -sub is_applicable +=item start_request_hook + +This is called immediately after setting up the basic request. The default +method simply returns C. + +Any other return value causes Maypole to abort further processing of the +request. This is useful for filtering out requests for static files, e.g. +images, which should not be processed by Maypole or by the templating engine: + + sub start_request_hook + { + my ($r) = @_; + + return Maypole::Constants::DECLINED if $r->path =~ /\.jpg$/; + return Maypole::Constants::OK; + } + +=cut + +sub start_request_hook { Maypole::Constants::OK } + +=item is_applicable + +B as of version 2.11. If you have overridden it, +please override C instead, and change the return type +from a Maypole:Constant to a true/false value. + +Returns a Maypole::Constant to indicate whether the request is valid. + +=item is_model_applicable + +Returns true or false to indicate whether the request is valid. + +The default implementation checks that C<< $r->table >> is publicly +accessible and that the model class is configured to handle the +C<< $r->action >>. + +=cut + +sub is_model_applicable { my ($self) = @_; + # cater for applications that are using obsolete version + if ($self->can('is_applicable')) + { + warn "DEPRECATION WARNING: rewrite is_applicable to the interface ". + "of Maypole::is_model_applicable\n"; + return $self->is_applicable == OK; + } + + # Establish which tables should be processed by the model my $config = $self->config; $config->ok_tables || $config->ok_tables( $config->display_tables ); $config->ok_tables( { map { $_ => 1 } @{ $config->ok_tables } } ) if ref $config->ok_tables eq "ARRAY"; + + my $ok_tables = $config->ok_tables; + # Does this request concern a table to be processed by the model? my $table = $self->table; - warn "We don't have that table ($table).\n" - . "Available tables are: " - . join( ",", @{ $config->display_tables } ) - if $self->debug - and not $config->ok_tables->{$table} - and $self->action; # this is probably always true - - return DECLINED unless exists $config->ok_tables->{$table}; + my $ok = 0; + + if (exists $ok_tables->{$table}) + { + $ok = 1; + } - # Is it public? - return DECLINED unless $self->model_class->is_public($self->action); + if (not $ok) + { + warn "We don't have that table ($table).\n" + . "Available tables are: " + . join( ",", keys %$ok_tables ) + if $self->debug and not $ok_tables->{$table}; + + return 0; + } + + # Is the action public? + my $action = $self->action; + return 1 if $self->model_class->is_public($action); + + warn "The action '$action' is not applicable to the table $table" + if $self->debug; - return OK; + return 0; } -# *only* intended for translating the return code from is_applicable() -sub __to_boolean { $_[0] == OK ? 1 : 0 } +=item get_session + +The default method is empty. + +=cut + +sub get_session { } + +=item call_authenticate + +This method first checks if the relevant model class +can authenticate the user, or falls back to the default +authenticate method of your Maypole application. + +=cut sub call_authenticate { @@ -271,6 +627,30 @@ sub call_authenticate return $self->authenticate($self); } +=item authenticate + +Returns a Maypole::Constant to indicate whether the user is authenticated for +the Maypole request. + +The default implementation returns C + +=cut + +sub authenticate { return OK } + + +=item call_exception + +This model is called to catch exceptions, first after authenticate, then after +processing the model class, and finally to check for exceptions from the view +class. + +This method first checks if the relevant model class +can handle exceptions the user, or falls back to the default +exception method of your Maypole application. + +=cut + sub call_exception { my ($self, $error) = @_; @@ -285,320 +665,389 @@ sub call_exception return $self->exception($error); } -sub additional_data { } +=item exception -sub authenticate { return OK } +This method is called if any exceptions are raised during the authentication or +model/view processing. It should accept the exception as a parameter and return +a Maypole::Constant to indicate whether the request should continue to be +processed. + +=cut sub exception { return ERROR } -sub parse_path -{ - my ($self) = @_; - - $self->path || $self->path('frontpage'); - - my @pi = grep {length} split '/', $self->path; - - $self->table(shift @pi); - - $self->action( shift @pi or 'index' ); - - $self->args(\@pi); -} +=item additional_data -# like CGI::param(), but read only -sub param -{ - my ($self, $key) = @_; - - return keys %{$self->params} unless defined $key; - - return unless exists $self->params->{$key}; - - my $val = $self->params->{$key}; - - return ref $val ? @$val : ($val) if wantarray; - - return ref $val ? $val->[0] : $val; -} +Called before the model processes the request, this method gives you a chance to +do some processing for each request, for example, manipulating C. -sub get_template_root {'.'} -sub get_request { } +=cut -sub parse_location { - die "Do not use Maypole directly; use Apache::MVC or similar"; -} +sub additional_data { } + +=item send_output + +Sends the output and additional headers to the user. + +=cut sub send_output { - die "Do not use Maypole directly; use Apache::MVC or similar"; + die "send_output is a virtual method. Do not use Maypole directly; use Apache::MVC or similar"; } -# Session and Repeat Submission Handling -sub make_random_id { - use Maypole::Session; - return Maypole::Session::generate_unique_id(); -} -=head1 NAME +=item redirect_request -Maypole - MVC web application framework +Sets output headers to redirect based on the arguments provided -=head1 SYNOPSIS +Accepts either a single argument of the full url to redirect to, or a hash of +named parameters : -See L. +$r->redirect_request('http://www.example.com/path'); -=head1 DESCRIPTION +or -This documents the Maypole request object. See the L, for a -detailed guide to using Maypole. +$r->redirect_request(protocol=>'https', domain=>'www.example.com', path=>'/path/file?arguments', status=>'302', url=>'..'); -Maypole is a Perl web application framework similar to Java's struts. It is -essentially completely abstracted, and so doesn't know anything about -how to talk to the outside world. +The named parameters are protocol, domain, path, status and url -To use it, you need to create a package which represents your entire -application. In our example above, this is the C package. +Only 1 named parameter is required but other than url, they can be combined as +required and current values (from the request) will be used in place of any +missing arguments. The url argument must be a full url including protocol and +can only be combined with status. -This needs to first use L which will make your package -inherit from the appropriate platform driver such as C or -C, and then call setup. This sets up the model classes and -configures your application. The default model class for Maypole uses -L to map a database to classes, but this can be changed by altering -configuration. (B calling setup.) +=cut -=head2 CLASS METHODS +sub redirect_request { + die "redirect_request is a virtual method. Do not use Maypole directly; use Apache::MVC or similar"; +} -=head3 config +=item redirect_internal_request -Returns the L object +=cut -=head3 setup +sub redirect_internal_request { - My::App->setup($data_source, $user, $password, \%attr); +} -Initialise the maypole application and model classes. Your application should -call this after setting configuration via L<"config"> +=back -=head3 init +=head2 Path processing and manipulation -You should not call this directly, but you may wish to override this to -add -application-specific initialisation. +=over 4 -=head3 new +=item path -Constructs a very minimal new Maypole request object. +Returns the request path -=head3 view_object +=item parse_path -Get/set the Maypole::View object +Parses the request path and sets the C, C and C +properties. Calls C before parsing path and setting properties. -=head3 debug +=cut - sub My::App::debug {1} +sub parse_path +{ + my ($self) = @_; + + # Previous versions unconditionally set table, action and args to whatever + # was in @pi (or else to defaults, if @pi is empty). + # Adding preprocess_path(), and then setting table, action and args + # conditionally, broke lots of tests, hence this: + $self->$_(undef) for qw/action table args/; + + $self->preprocess_path; -Returns the debugging flag. Override this in your application class to -enable/disable debugging. + $self->path || $self->path('frontpage'); + + my @pi = grep {length} split '/', $self->path; + + $self->table || $self->table(shift @pi); + $self->action || $self->action( shift @pi or 'index' ); + $self->args || $self->args(\@pi); +} -=head2 INSTANCE METHODS +=item preprocess_path -=head3 parse_location +Sometimes when you don't want to rewrite or over-ride parse_path but +want to rewrite urls or extract data from them before it is parsed. -Turns the backend request (e.g. Apache::MVC, Maypole, CGI) into a -Maypole -request. It does this by setting the C, and invoking C -and -C. +This method is called after parse_location has populated the request +information and before parse_path has populated the model and action +information, and is passed the request object. -You should only need to define this method if you are writing a new -Maypole -backend. +You can set action, args or table in this method and parse_path will +then leave those values in place or populate them if not present -=head3 path +=cut -Returns the request path +sub preprocess_path { }; -=head3 parse_path +=item make_path( %args or \%args or @args ) -Parses the request path and sets the C, C and C
-properties +This is the counterpart to C. It generates a path to use +in links, form actions etc. To implement your own path scheme, just override +this method and C. -=head3 table + %args = ( table => $table, + action => $action, + additional => $additional, # optional - generally an object ID + ); + + \%args = as above, but a ref + + @args = ( $table, $action, $additional ); # $additional is optional -The table part of the Maypole request path +C can be used as an alternative key to C. -=head3 action +C<$additional> can be a string, an arrayref, or a hashref. An arrayref is +expanded into extra path elements, whereas a hashref is translated into a query +string. -The action part of the Maypole request path +=cut -=head3 args +sub make_path +{ + my $r = shift; + + my %args; + + if (@_ == 1 and ref $_[0] and ref $_[0] eq 'HASH') + { + %args = %{$_[0]}; + } + elsif ( @_ > 1 and @_ < 4 ) + { + $args{table} = shift; + $args{action} = shift; + $args{additional} = shift; + } + else + { + %args = @_; + } + + do { die "no $_" unless $args{$_} } for qw( table action ); -A list of remaining parts of the request path after table and action -have been -removed + my $additional = $args{additional} || $args{id}; + + my @add = (); + + if ($additional) + { + # if $additional is a href, make_uri() will transform it into a query + @add = (ref $additional eq 'ARRAY') ? @$additional : ($additional); + } + + my $uri = $r->make_uri($args{table}, $args{action}, @add); + + return $uri->as_string; +} -=head3 headers_in -A L object containing HTTP headers for the request -=head3 headers_out +=item make_uri( @segments ) -A L object that contains HTTP headers for the output +Make a L object given table, action etc. Automatically adds +the C. -=head3 parse_args +If the final element in C<@segments> is a hash ref, C will render it +as a query string. -Turns post data and query string paramaters into a hash of C. +=cut -You should only need to define this method if you are writing a new -Maypole -backend. +sub make_uri +{ + my ($r, @segments) = @_; -=head3 param + my $query = (ref $segments[-1] eq 'HASH') ? pop(@segments) : undef; + + my $base = $r->config->uri_base; + $base =~ s|/$||; + + my $uri = URI->new($base); + $uri->path_segments($uri->path_segments, grep {length} @segments); + + my $abs_uri = $uri->abs('/'); + $abs_uri->query_form($query) if $query; + return $abs_uri; +} -An accessor for request parameters. It behaves similarly to CGI::param() for -accessing CGI parameters. +=item parse_args -=head3 params +Turns post data and query string paramaters into a hash of C. -Returns a hash of request parameters. The source of the parameters may vary -depending on the Maypole backend, but they are usually populated from request -query string and POST data. +You should only need to define this method if you are writing a new Maypole +backend. -B Where muliple values of a parameter were supplied, the -C -value -will be an array reference. +=back -=head3 get_template_root +=head2 Request properties -Implementation-specific path to template root. +=over 4 -You should only need to define this method if you are writing a new -Maypole -backend. Otherwise, see L +=item model_class -=head3 get_request +Returns the perl package name that will serve as the model for the +request. It corresponds to the request C
attribute. -You should only need to define this method if you are writing a new -Maypole backend. It should return something that looks like an Apache -or CGI request object, it defaults to blank. +=item objects -=head3 is_applicable +Get/set a list of model objects. The objects will be accessible in the view +templates. -Returns a Maypole::Constant to indicate whether the request is valid. +If the first item in C<$self-Eargs> can be Cd by the model +class, it will be removed from C and the retrieved object will be added to +the C list. See L for more information. -The default implementation checks that C<$self-Etable> is publicly -accessible -and that the model class is configured to handle the C<$self-Eaction> +=item template_args -=head3 authenticate + $self->template_args->{foo} = 'bar'; -Returns a Maypole::Constant to indicate whether the user is -authenticated for -the Maypole request. +Get/set a hash of template variables. -The default implementation returns C +=item stash -=head3 model_class +A place to put custom application data. Not used by Maypole itself. -Returns the perl package name that will serve as the model for the -request. It corresponds to the request C
attribute. +=item template -=head3 additional_data +Get/set the template to be used by the view. By default, it returns +C<$self-Eaction> -Called before the model processes the request, this method gives you a -chance -to do some processing for each request, for example, manipulating -C. -=head3 objects +=item error -Get/set a list of model objects. The objects will be accessible in the -view -templates. +Get/set a request error -If the first item in C<$self-Eargs> can be Cd by the model -class, -it will be removed from C and the retrieved object will be added -to the -C list. See L for more information. +=item output -=head3 template_args +Get/set the response output. This is usually populated by the view class. You +can skip view processing by setting the C. - $self->template_args->{foo} = 'bar'; +=item table -Get/set a hash of template variables. +The table part of the Maypole request path -=head3 template +=item action -Get/set the template to be used by the view. By default, it returns -C<$self-Eaction> +The action part of the Maypole request path -=head3 exception +=item args -This method is called if any exceptions are raised during the -authentication -or -model/view processing. It should accept the exception as a parameter and -return -a Maypole::Constant to indicate whether the request should continue to -be -processed. +A list of remaining parts of the request path after table and action +have been +removed -=head3 error +=item headers_in -Get/set a request error +A L object containing HTTP headers for the request -=head3 output +=item headers_out -Get/set the response output. This is usually populated by the view -class. You -can skip view processing by setting the C. +A L object that contains HTTP headers for the output -=head3 document_encoding +=item document_encoding Get/set the output encoding. Default: utf-8. -=head3 content_type +=item content_type Get/set the output content type. Default: text/html -=head3 send_output +=item get_protocol -Sends the output and additional headers to the user. +Returns the protocol the request was made with, i.e. https -=head3 call_authenticate +=cut -This method first checks if the relevant model class -can authenticate the user, or falls back to the default -authenticate method of your Maypole application. +sub get_protocol { + die "get_protocol is a virtual method. Do not use Maypole directly; use Apache::MVC or similar"; +} +=back -=head3 call_exception +=head2 Request parameters -This model is called to catch exceptions, first after authenticate, then after -processing the model class, and finally to check for exceptions from the view -class. +The source of the parameters may vary depending on the Maypole backend, but they +are usually populated from request query string and POST data. -This method first checks if the relevant model class -can handle exceptions the user, or falls back to the default -exception method of your Maypole application. +Maypole supplies several approaches for accessing the request parameters. Note +that the current implementation (via a hashref) of C and C is +likely to change in a future version of Maypole. So avoid direct access to these +hashrefs: + + $r->{params}->{foo} # bad + $r->params->{foo} # better + + $r->{query}->{foo} # bad + $r->query->{foo} # better + + $r->param('foo') # best + +=over 4 + +=item param + +An accessor (get or set) for request parameters. It behaves similarly to +CGI::param() for accessing CGI parameters, i.e. -=head3 make_random_id + $r->param # returns list of keys + $r->param($key) # returns value for $key + $r->param($key => $value) # returns old value, sets to new value + +=cut + +sub param +{ + my ($self, $key) = (shift, shift); + + return keys %{$self->params} unless defined $key; + + return unless exists $self->params->{$key}; + + my $val = $self->params->{$key}; + + if (@_) + { + my $new_val = shift; + $self->params->{$key} = $new_val; + } + + return ref $val ? @$val : ($val) if wantarray; + + return ref $val ? $val->[0] : $val; +} + + +=item params + +Returns a hashref of request parameters. + +B Where muliple values of a parameter were supplied, the C value +will be an array reference. + +=item query + +Alias for C. + +=item make_random_id returns a unique id for this request can be used to prevent or detect repeat submissions. -=head3 handler - -This method sets up the class if it's not done yet, sets some -defaults and leaves the dirty work to handler_guts. +=cut -=head3 handler_guts +# Session and Repeat Submission Handling +sub make_random_id { + use Maypole::Session; + return Maypole::Session::generate_unique_id(); +} -This is the core of maypole. You don't want to know. +=back =head1 SEE ALSO @@ -611,7 +1060,7 @@ L, L, L. =head1 AUTHOR -Maypole is currently maintained by Simon Flack C +Maypole is currently maintained by Aaron Trevena =head1 AUTHOR EMERITUS