| CGI-Prototype documentation | Contained in the CGI-Prototype distribution. |
CGI::Prototype::Hidden - Create a CGI application by subclassing - hidden field
# in My/App.pm --- package My::App; use base qw(CGI::Prototype::Hidden); # in /some/cgi-bin/program use lib qw(/location); use My::App; My::App->activate;
CGI::Prototype::Hidden extends CGI::Prototype by providing a hidden field mechanism for state, and a dispatching algorithm based on that hidden field. In particular,
Dispatching to a particular paged based on the "state" of the application is performed according to param field.
The name of the state is appended to an application-wide package prefix to determine an appropriate class to handle the request.
The package for the class is autoloaded if needed.
The template for the class replaces .pm with .tt (configurable),
found in the same @INC path, and is therefore likely to be in the
same directory.
A "wrapper" template is automatically provided.
Thus, a simple 10-page CGI application will require 23 files: 10 classes, 10 corresponding templates, a wrapper template, a master application class, and a CGI script that loads the master application class and activates it.
The default class is My::App, but this can be overridden. The
default state is welcome, but this too can be overridden. The
default hidden param name for the state is _state, and if you think
this can be overridden, you are correct. See the trend here?
A sample app is the best way to show all of this, of course. We don't have one yet... that's on the TODO list. However, the functions have all been exercised in the tests for this module, including an artificial application, so check that out for at least an example of the interfaces.
These methods or values are the ones you'll most likely change in your application, although you can leave them all alone and it'll still be a valid framework to create your entire application.
The name of the hidden field which will contain the state, defaulting
to _state.
In any form you create, or any constructed URL, you must be sure to include this param as part of the form so that the right response can be matched up for the submitted data. For example:
<form> [% self.CGI.hidden(self.config_state_param) %] First name:[% self.CGI.textfield("first_name") %]<br> Last name: [% self.CGI.textfield("last_name") %] <input type=submit> </form>
The class prefix placed ahead of the state name, default My::App.
For example, the controller class for the welcome state will be
<My::App::welcome>.
You should change this if you are using mod_perl to something that won't conflict with other usages of the same server space. For CGI scripts, the default is an easy classname to remember.
Note that the template also use this name as their prefix, so that your controller and template files end up in the same directory.
The initial page if the state is missing, default welcome.
The name of the WRAPPER template, default My/App/WRAPPER.tt.
If you change config_class_prefix, you'll want to change this as
well so that WRAPPER.tt ends up in the right directory. (I debated
doing that for you so you could just say "WRAPPER.TT", but that'd
make more complicated versions of this callback be even more and more
complicated.)
The wrapper template is called with template set to the wrapped
template, which should be processed in the wrapper. The smallest
wrapper is therefore:
[% PROCESS $template %]
However, typically, you'll want to define app-wide blocks and variables, and maybe wrap the statement above in an exception catcher. For example:
[%-
TRY;
content = PROCESS $template;
self.CGI.header;
self.CGI.start_html;
content;
self.CGI.end_html;
### exceptions
## for errors:
CATCH;
CLEAR;
self.CGI.header('text/plain');
-%]
An error has occurred. Remain calm.
Authorities have been notified. Do not leave the general area.
[%-
FILTER stderr -%]
** [% template.filename %] error: [% error.info %] **
[%
END; # FILTER
END; # TRY
-%]
This sends back a plain message to the browser, as well as logging the
precise error text to STDERR, and hopefully the web error log.
The location of the compiled Perl templates, default
"/tmp/compile-dir.$<" (where $< is the current user's numeric
user ID). You'll want this to be some place that the process can
write, but nobody else can. The default is functional, but not immune
to other hostile users on the same box, so you'll want to override
that for those cases.
The suffix replacing .pm when the module name is mapped
to the template name. By default, it's .tt.
You will most likely not need to change these, but you'll want to stay away from their names.
Called with a page name, returns a page object. Will also autoload the package.
This is still an experimental feature that will be reworked in future releases.
Called with a page name, returns a new page object that can be used as
self in a template, mixing in the code from the page's class for
additional heavy lifting.
For example, to have a "subpage" plugin, create a subpage.tt
and subpage.pm file, then include the tt with:
[% INCLUDE My/App/subpage.tt
self = self.plugin("subpage")
other = parms
go = here
%]
Now, within subpage.tt, calls to self.SomeMethod will first
search the original page's lineage, and then the plugin class lineage
for a definition for SomeMethod.
Overridden from CGI::Prototype. Selects either the hidden field state, or the default state, and returns the page object.
Returns the simple name for the current page object by stripping off
the config_class_prefix. Note that this will fail in the event of
prototype page constructed on the fly, rather than a named class.
Hmm, I'll have to think about what that implies.
Overridden from CGI::Prototype. Forces the hidden state param to
the shortname of the current object, then calls
render_enter_per_page.
If you need page-specific render_enter items, put them here. The
default definition does nothing. This is to keep from having to call
superclass methods for render_enter.
Overridden from CGI::Prototype. Calls respond_per_app and then
respond_per_page, looking for a true value, which is then returned.
If you have site-wide buttons (like a button-bar on the side or top of
your form), look for them in respond_per_app, and return the new
page from there. Otherwise, return undef, and it'll fall through
to the per-page response.
A hook for application-wide responses, defaulting to undef. Should
return either a page object (to be rendered) or a false value
(selecting the per-page respond).
If respond_per_app returns false, this hook is then evaluated. It
should return a page object to be rendered. The default returns the
current page object, so you "stay here" for rendering.
Overridden from CGI::Prototype. Returns the name of a template,
defined by replacing the double-colons in the classname of the current
page with forward slashes, and then appending .tt (by default, see
config_tt_extension). Because @INC is added to the
INCLUDE_PATH for the engine, this should find the .tt file in
the same directory as the .pm file.
Overridden from CGI::Prototype, so that the cached Template object that is essentially:
Template->new
(
POST_CHOMP => 1,
INCLUDE_PATH => [@INC],
COMPILE_DIR => $self->config_compile_dir,
PROCESS => [$self->config_wrapper],
)
CGI::Prototype, Template::Manual
Please report any bugs or feature requests to bug-cgi-prototype@rt.cpan.org, or through the web interface at http://rt.cpan.org. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
Randal L. Schwartz, <merlyn@stonehenge.com>
Special thanks to Geekcruises.com and an unnamed large university for providing funding for the development of this module.
Copyright (C) 2003, 2004, 2005 by Randal L. Schwartz
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available.
| CGI-Prototype documentation | Contained in the CGI-Prototype distribution. |
package CGI::Prototype::Hidden; our $_mirror = __PACKAGE__->reflect; use base CGI::Prototype; use strict;
sub config_state_param { "_state" }
sub config_class_prefix { "My::App" }
sub config_default_page { "welcome" }
sub config_wrapper { "My/App/WRAPPER.tt" }
sub config_compile_dir { "/tmp/compile-dir.$<" }
sub config_tt_extension { ".tt" }
sub name_to_page { my $self = shift; my $page = shift; die "bad page name: $page" unless $page and $page =~ /^([\w]+)$/; $page = $1; my $package = $self->config_class_prefix; $package .= "::$page"; eval "require $package" unless eval "defined %${package}::"; die if $@; return $package->reflect->object; }
sub plugin { my $self = shift; my $name = shift; return $self->new('*' => $self->name_to_page($name)); }
sub dispatch { my $self = shift; my $name = $self->param($self->config_state_param) || $self->config_default_page; return $self->name_to_page($name); }
sub shortname { my $self = shift; my $package = ref $self || $self; my $prefix = $self->config_class_prefix; $package =~ /^${prefix}::(\w+)$/ or die "name mismatch for $package!"; "$1"; }
sub render_enter { my $self = shift; $self->param($self->config_state_param, $self->shortname); $self->render_enter_per_page; # additional hook }
sub render_enter_per_page { } # default action is nothing
sub respond { my $self = shift; return $self->respond_per_app || $self->respond_per_page; }
sub respond_per_app { undef } # respond to app-wide buttons
sub respond_per_page { return shift; # default is stay here }
sub template { my $self = shift; my $mirror = $self->reflect; my $package = $mirror->package; ## default template is classname with ".tt": (my $template = $package) =~ s{::}{/}g; $template .= $self->config_tt_extension; return $template; }
sub engine_config { my $self = shift; return { POST_CHOMP => 1, INCLUDE_PATH => [@INC], COMPILE_DIR => $self->config_compile_dir, PROCESS => [$self->config_wrapper], }; }
1;