]>
Dogcows Code - chaz/p5-CGI-Ex/blob - lib/CGI/Ex/App.pm
3 ### CGI Extended Application
5 ###----------------------------------------------------------------###
6 # Copyright 2004 - Paul Seamons #
7 # Distributed under the Perl Artistic License without warranty #
8 ###----------------------------------------------------------------###
10 ### See perldoc at bottom
15 $EXT_PRINT $EXT_VAL $BASE_DIR_REL $BASE_DIR_ABS $BASE_NAME_MODULE
20 use CGI
::Ex
::Dump
qw(debug);
23 ### Default file locations
24 ### these are used for the provided stub functions - if you are not
25 ### using the stub functions - then you won't need to worry about these
26 $EXT_PRINT ||= 'html';
28 $BASE_DIR_REL ||= ''; # relative path - stub methods will look in $BASE_DIR_REL/dir/of/content.html
29 $BASE_DIR_ABS ||= ''; # content should be found at "$BASE_DIR_ABS/$BASE_DIR_REL/dir/of/content.html"
30 $BASE_NAME_MODULE ||= ''; # the cgi name
32 ### list of modules to exclude during cleanup
33 ### this takes care of situations such as
34 ### template toolkits rules area which contains
35 ### a nested structure of rules and sub references.
36 $CLEANUP_EXCLUDE{'Template::Parser'} = 1;
40 ###----------------------------------------------------------------###
43 my $class = shift || __PACKAGE__
;
44 my $self = ref($_[0]) ? shift : {@_};
52 ###----------------------------------------------------------------###
56 my $args = ref($_[0]) ? shift : {@_};
57 $self = $self->new($args) if ! ref $self;
61 ### a chance to do things at the very beginning
62 return $self if $self->pre_navigate;
66 local $self->{'__morph_lineage_start_index'} = $#{$self->{'__morph_lineage'} || []};
70 ### rethrow the error unless we long jumped out of recursive nav_loop calls
71 die $@ if $@ ne "Long Jump\n";
74 ### one chance to do things at the very end
79 ### catch errors - if any
81 $self->handle_error($@);
90 ### keep from an infinate nesting
91 local $self->{recurse
} = $self->{recurse
} || 0;
92 if ($self->{recurse
} ++ >= $self->recurse_limit) {
93 my $err = "recurse_limit reached (".$self->recurse_limit.")";
94 $err .= " number of jumps (".$self->{jumps
}.")" if ($self->{jumps
} || 0) > 1;
98 ### get the path (simple arrayref based thing)
99 my $path = $self->path;
101 ### allow for an early return
102 return if $self->pre_loop($path); # a true value means to abort the navigate
104 ### get a hash of valid paths (if any)
105 my $valid_steps = $self->valid_steps;
107 ### iterate on each step of the path
108 foreach ($self->{path_i
} ||= 0;
109 $self->{path_i
} <= $#$path;
110 $self->{path_i
} ++) {
111 my $step = $path->[$self->{path_i
}];
112 next if $step !~ /^[a-zA-Z_]\w*$/; # don't process the step if it contains odd characters
114 ### check if this is an allowed step
116 if (! $valid_steps->{$step}
117 && $step ne $self->default_step
118 && $step ne 'forbidden') {
119 $self->stash->{'forbidden_step'} = $step;
120 $self->replace_path('forbidden');
125 ### allow for becoming another package (allows for some steps in external files)
128 ### run the guts of the step
129 my $status = $self->run_hook('run_step', $step);
131 $self->unmorph($step);
133 ### Allow for the run_step to intercept.
134 ### A true status means the run_step took over navigation.
138 ### allow for one exit point after the loop
139 return if $self->post_loop($path); # a true value means to abort the navigate
141 ### run the default step as a last resort
142 $self->insert_path($self->default_step);
143 $self->nav_loop; # go recursive
152 sub recurse_limit
{ shift-
>{'recurse_limit'} || $RECURSE_LIMIT || 15 }
158 ### if the pre_step exists and returns true, exit the nav_loop
159 return 1 if $self->run_hook('pre_step', $step);
161 ### allow for skipping this step (but stay in the nav_loop)
162 return 0 if $self->run_hook('skip', $step);
164 ### see if we have complete valid information for this step
165 ### if so, do the next step
166 ### if not, get necessary info and print it out
167 if ( ! $self->run_hook('prepare', $step, 1)
168 || ! $self->run_hook('info_complete', $step)
169 || ! $self->run_hook('finalize', $step, 1)) {
171 ### show the page requesting the information
172 $self->run_hook('prepared_print', $step);
174 ### a hook after the printing process
175 $self->run_hook('post_print', $step);
180 ### a hook before end of loop
181 ### if the post_step exists and returns true, exit the nav_loop
182 return 1 if $self->run_hook('post_step', $step);
184 ### let the nav_loop continue searching the path
188 ### standard functions for printing - gather information
193 my $hash_base = $self->run_hook('hash_base', $step);
194 my $hash_comm = $self->run_hook('hash_common', $step);
195 my $hash_form = $self->run_hook('hash_form', $step);
196 my $hash_fill = $self->run_hook('hash_fill', $step);
197 my $hash_swap = $self->run_hook('hash_swap', $step);
198 my $hash_errs = $self->run_hook('hash_errors', $step);
199 $_ ||= {} foreach $hash_base, $hash_comm, $hash_form, $hash_fill, $hash_swap, $hash_errs;
202 $hash_errs->{$_} = $self->format_error($hash_errs->{$_})
203 foreach keys %$hash_errs;
204 $hash_errs->{has_errors
} = 1 if scalar keys %$hash_errs;
206 ### layer hashes together
207 my $fill = {%$hash_form, %$hash_base, %$hash_comm, %$hash_fill};
208 my $swap = {%$hash_form, %$hash_base, %$hash_comm, %$hash_swap, %$hash_errs};
209 $fill = {} if $self->no_fill($step);
211 ### run the print hook - passing it the form and fill info
212 $self->run_hook('print', $step, undef,
216 sub no_fill
{ shift-
>{'no_fill'} }
222 if (my $ref = $self->{'__morph_lineage'}) {
223 ### use the saved index - this allows for early "morphers" to only get rolled back so far
224 my $index = $self->{'__morph_lineage_start_index'};
225 $index = -1 if ! defined $index;
226 $self->unmorph while $#$ref != $index;
235 my $i = ($#_ == -1) ? 1 : shift;
236 my $path = $self->path;
237 my $path_i = $self->{path_i
};
238 die "Can't jump if nav_loop not started" if ! defined $path_i;
240 ### validate where we are jumping to
244 } elsif ($i eq 'LAST') {
245 $i = $#$path - $path_i;
246 } elsif ($i eq 'NEXT') {
248 } elsif ($i eq 'CURRENT') {
250 } elsif ($i eq 'PREVIOUS') {
252 } else { # look for a step by that name
253 for (my $j = $#$path; $j >= 0; $j --) {
254 if ($path->[$j] eq $i) {
261 if ($i !~ /^-?\d+$/) {
263 Carp
::croak
("Invalid jump index ($i)");
266 ### manipulate the path to contain the new jump location
268 my $cut_i = $path_i + $i;
269 if ($cut_i > $#$path) {
270 push @replace, $self->default_step;
271 } elsif ($cut_i < 0) {
272 push @replace, @$path;
274 push @replace, @$path[$cut_i .. $#$path];
276 $self->replace_path(@replace);
278 ### record the number of jumps
279 $self->{jumps
} ||= 0;
282 ### run the newly fixed up path (recursively)
283 $self->{path_i
} ++; # move along now that the path is updated
285 $self->exit_nav_loop;
290 return $self->{'default_step'} || 'main';
293 ###----------------------------------------------------------------###
297 return $self->{'step_key'} || 'step';
300 ### determine the path to follow
303 return $self->{path
} ||= do {
304 my @path = (); # default to empty path
305 my $step_key = $self->step_key;
307 if (my $step = $self->form->{$step_key}) {
309 } elsif ($ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} =~ m
|^/(\w
+)|) {
313 \
@path; # return of the do
317 ### really should only be used during initialization
320 my $path = $self->{path
} ||= [];
321 die "Cannot call set_path after the navigation loop has begun" if $self->{path_i
};
322 splice @$path, 0, $#$path + 1, @_; # change entries in the ref
325 ### legacy - same as append_path
328 push @{ $self->path }, @_;
331 ### append entries onto the end
334 push @{ $self->path }, @_;
337 ### replace all entries that are left
340 my $ref = $self->path;
341 my $i = $self->{path_i
} || 0;
342 if ($i + 1 > $#$ref) {
345 splice(@$ref, $i + 1, $#$ref - $i, @_); # replace remaining entries
349 ### insert more steps into the current path
352 my $ref = $self->path;
353 my $i = $self->{path_i
} || 0;
354 if ($i + 1 > $#$ref) {
357 splice(@$ref, $i + 1, 0, @_); # insert a path at the current location
361 ### a hash of paths that are allowed, default undef is all
364 ###----------------------------------------------------------------###
365 ### allow for checking where we are in the path
367 sub step_by_path_index
{
370 my $ref = $self->path;
372 return $self->default_step if $i > $#$ref;
378 die "previous_step is readonly" if $#_ != -1;
379 return $self->step_by_path_index( ($self->{path_i
} || 0) - 1 );
384 die "current_step is readonly" if $#_ != -1;
385 return $self->step_by_path_index( ($self->{path_i
} || 0) );
390 die "next_step is readonly" if $#_ != -1;
391 return $self->step_by_path_index( ($self->{path_i
} || 0) + 1 );
396 die "last_step is readonly" if $#_ != -1;
397 return $self->step_by_path_index( $#{ $self->path } );
402 die "first_step is readonly" if $#_ != -1;
403 return $self->step_by_path_index( 0 );
406 ###----------------------------------------------------------------###
411 ### return the appropriate hook to call
414 my $hook = shift || do { require Carp
; Carp
::confess
("Missing hook name") };
415 my $step = shift || '';
417 my $hist = $self->history;
419 if ($step && ($code = $self->can("${step}_${hook}"))) {
420 push @$hist, "$step - $hook - ${step}_${hook}";
422 } elsif ($code = $self->can($hook)) {
423 push @$hist, "$step - $hook - $hook";
425 } elsif (UNIVERSAL
::isa
($default, 'CODE')) {
426 push @$hist, "$step - $hook - DEFAULT CODE";
429 push @$hist, "$step - $hook - DEFAULT";
430 return sub { return $default };
436 ### get and call the appropriate hook
442 my $code = $self->hook($hook, $step, $default);
443 return $self->$code($step, @_);
447 return shift-
>{'history'} ||= [];
450 ### default die handler - show what happened and die (so its in the error logs)
454 debug
$err, $self->path, $self->history;
458 ###----------------------------------------------------------------###
459 ### utility modules for jeckyl/hyde on self
463 return $self->{'allow_morph'} ? 1 : 0;
466 sub allow_nested_morph
{
468 return $self->{'allow_nested_morph'} ? 1 : 0;
473 my $step = shift || return;
474 return if ! (my $allow = $self->allow_morph); # not true
476 ### place to store the lineage
477 my $lin = $self->{'__morph_lineage'} ||= [];
478 my $cur = ref $self; # what are we currently
479 push @$lin, $cur; # store so subsequent unmorph calls can do the right thing
480 my $hist = $self->history;
481 push @$hist, "$step - morph - morph";
482 my $sref = \
$hist->[-1]; # get ref so we can add more info in a moment
484 if (ref($allow) && ! $allow->{$step}) { # hash - but no step - record for unbless
485 $$sref .= " - not allowed to morph to that step";
489 ### make sure we haven't already been reblessed
490 if ($#$lin != 0 # is this the second morph call
491 && (! ($allow = $self->allow_nested_morph) # not true
492 || (ref($allow) && ! $allow->{$step}) # hash - but no step
494 $$sref .= $allow ? " - not allowed to nested_morph to that step" : " - nested_morph disabled";
495 return; # just return - don't die so that we can morph early
498 ### if we are not already that package - bless us there
499 my $new = $self->run_hook('morph_package', $step);
501 my $file = $new .'.pm';
503 if (UNIVERSAL
::can
($new, 'can') # check if the package space exists
504 || eval { require $file }) { # check for a file that holds this package
505 ### become that package
507 $$sref .= " - changed $cur to $new";
508 if (my $method = $self->can('fixup_after_morph')) {
509 $self->$method($step);
513 if ($@ =~ /^\s*(Can\'t locate \S+ in \@INC)/) { # let us know what happened
514 $$sref .= " - failed from $cur to $new: $1";
516 $$sref .= " - failed from $cur to $new: $@";
517 my $err = "Trouble while morphing to $file: $@";
529 my $step = shift || '__no_step';
530 my $lin = $self->{'__morph_lineage'} || return;
532 my $prev = pop(@$lin) || die "unmorph called more times than morph - current ($cur)";
534 ### if we are not already that package - bless us there
535 my $hist = $self->history;
537 if (my $method = $self->can('fixup_before_unmorph')) {
538 $self->$method($step);
541 push @$hist, "$step - unmorph - unmorph - changed from $cur to $prev";
543 push @$hist, "$step - unmorph - unmorph - already isa $cur";
549 ###----------------------------------------------------------------###
550 ### allow for cleanup including deep nested objects
554 ref($self)->cleanup_cross_references($self);
557 sub cleanup_cross_references
{
560 my $seen = shift || {};
561 return if $seen->{$self}; # prevent recursive checking
563 return if $CLEANUP_EXCLUDE{ ref($self) };
564 if (UNIVERSAL
::isa
($self, 'HASH')) {
565 require Scalar
::Util
; # first self will always be hash
566 foreach my $key (keys %$self) {
567 next if ! $self->{$key};
568 $class->cleanup_cross_references($self->{$key}, $seen);
569 # weaken and remove blessed objects
570 # this will clober objects in global caches that are referenced in the structure
571 # so beware (that means weaken your cached references)
572 if (Scalar
::Util
::blessed
($self->{$key})
573 && ! Scalar
::Util
::isweak
($self->{$key})) {
574 Scalar
::Util
::weaken
($self->{$key});
575 $self->{$key} = undef;
576 } elsif (UNIVERSAL
::isa
($self->{$key}, 'CODE')) {
577 $self->{$key} = undef;
580 } elsif (UNIVERSAL
::isa
($self, 'ARRAY')) {
581 for my $key (0 .. $#$self) {
582 next if ! $self->[$key];
583 $class->cleanup_cross_references($self->[$key], $seen);
584 if (Scalar
::Util
::blessed
($self->[$key])
585 && ! Scalar
::Util
::isweak
($self->[$key])) {
586 Scalar
::Util
::weaken
($self->[$key]);
587 $self->[$key] = undef;
588 } elsif (UNIVERSAL
::isa
($self->[$key], 'CODE')) {
589 $self->[$key] = undef;
595 ###----------------------------------------------------------------###
596 ### a few standard base accessors
601 $self->{form
} = shift || die "Invalid form";
603 return $self->{form
} ||= $self->cgix->get_form;
609 $self->{cookies
} = shift || die "Invalid cookies";
611 return $self->{cookies
} ||= $self->cgix->get_cookies;
616 return $self->{cgix
} ||= do {
617 my $args = shift || {};
619 CGI
::Ex-
>new($args); # return of the do
625 $self->{cgix
} = shift;
630 return $self->{vob
} ||= do {
631 my $args = shift || {};
632 $args->{cgix
} ||= $self->cgix;
633 require CGI
::Ex
::Validate
;
634 CGI
::Ex
::Validate-
>new($args); # return of the do
640 $self->{vob
} = shift;
645 return $self->{auth
} ||= do {
646 my $args = shift || {};
647 $args->{cgix
} ||= $self->cgix,
648 $args->{form
} ||= $self->form,
649 $args->{cookies
} ||= $self->cookies,
650 require CGI
::Ex
::Auth
;
651 CGI
::Ex
::Auth-
>new($args); # return of the do
657 $self->{auth
} = shift;
660 ### provide a place for placing variables
663 return $self->{'stash'} ||= {};
666 ### allow for adding arbitrary values to self
670 my $key = '__prop_'. $prop;
671 my $name = __PACKAGE__
."::". $prop;
673 *$name = sub : lvalue
{
675 $self->{$key} = shift() if $#_ != -1;
677 } if ! defined &$name;
678 $self->$prop(shift()) if $#_ != -1;
681 ###----------------------------------------------------------------###
682 ### js_validation items
684 ### creates javascript suitable for validating the form
688 return '' if $self->ext_val eq 'htm'; # let htm validation do it itself
690 my $form_name = shift || $self->run_hook('form_name', $step);
691 my $hash_val = shift || $self->run_hook('hash_validation', $step, {});
692 my $js_uri = $self->js_uri_path;
693 return '' if UNIVERSAL
::isa
($hash_val, 'HASH') && ! scalar keys %$hash_val
694 || UNIVERSAL
::isa
($hash_val, 'ARRAY') && $#$hash_val == -1;
696 return $self->vob->generate_js($hash_val, $form_name, $js_uri);
699 ### where to find the javascript files
700 ### default to using this script as a handler
703 my $script = $ENV{'SCRIPT_NAME'} || die "Missing SCRIPT_NAME";
704 return ($self->can('path') == \
&CGI
::Ex
::App
::path
)
705 ? $script . '/js' # try to use a cache friendly URI (if path is our own)
706 : $script . '?'.$self->step_key.'=js&js='; # use one that works with more paths
709 ### name to attach js validation to
710 sub form_name
{ 'theform' }
712 ### provide some rudimentary javascript support
713 ### if valid_steps is defined - it should include "js"
717 ### make sure path info looks like /js/CGI/Ex/foo.js
718 my $file = $self->form->{'js'} || $ENV{'PATH_INFO'} || '';
719 $file = ($file =~ m!^(?:/js/|/)?(\w+(?:/\w+)*\.js)$!) ? $1 : '';
721 $self->cgix->print_js($file);
722 return 1; # intercepted
725 ###----------------------------------------------------------------###
726 ### implementation specific subs
732 INCLUDE_PATH
=> $self->base_dir_abs,
742 ### get a filename relative to base_dir_abs
743 my $file = $self->run_hook('file_print', $step);
746 my $t = Template-
>new($self->template_args($step));
748 ### process the document
750 my $status = $t->process($file, $swap, \
$out) || die $Template::ERROR
;
752 ### fill in any forms
753 $self->cgix->fill(\
$out, $fill) if $fill && ! $self->{no_fill
};
756 $self->cgix->print_content_type();
762 $self->{base_dir_rel
} = shift if $#_ != -1;
763 return $self->{base_dir_rel
} ||= $BASE_DIR_REL;
768 $self->{base_dir_abs
} = shift if $#_ != -1;
769 return $self->{base_dir_abs
} || $BASE_DIR_ABS
770 || die "\$BASE_DIR_ABS not set for use in stub functions";
775 $self->{ext_val
} = shift if $#_ != -1;
776 return $self->{ext_val
} || $EXT_VAL || die "\$EXT_VAL not set for use in stub functions";
781 $self->{ext_print
} = shift if $#_ != -1;
782 return $self->{ext_print
} || $EXT_PRINT || die "\$EXT_PRINT not set for use in stub functions";
787 return 1 if scalar keys %{ $self->hash_errors };
793 # return $error if $error =~ /<span/i;
794 # return "<span class=\"error\">$error</span>";
797 ###----------------------------------------------------------------###
798 ### default stub subs
800 ### used for looking up a module to morph into
803 my $step = shift || '';
804 my $cur = ref $self; # default to using self as the base for morphed modules
805 my $new = $cur .'::'. $step;
806 $new =~ s/(\b|_+)(\w)/\u$2/g; # turn Foo::my_step_name into Foo::MyStepName
810 sub base_name_module
{
812 $self->{base_name_module
} = shift if $#_ != -1;
813 return $self->{base_name_module
} ||= $BASE_NAME_MODULE;
816 ### used for looking up template content
819 my $step = shift || '';
821 if ($name = $self->base_name_module) {
824 return ($0 =~ m/(\w+)(\.\w+)?$/) ? $1 # allow for cgi-bin/foo or cgi-bin/foo.pl
825 : die "Couldn't determine module name from \"name_module\" lookup ($step)";
829 ### which file is used for templating
834 my $base_dir_rel = $self->base_dir_rel;
835 my $module = $self->run_hook('name_module', $step);
836 my $_step = $self->run_hook('name_step', $step, $step);
837 my $ext = $self->ext_print;
839 return "$base_dir_rel/$module/$_step.$ext";
842 ### which file is used for validation
847 my $base_dir = $self->base_dir_rel;
848 my $module = $self->run_hook('name_module', $step);
849 my $_step = $self->run_hook('name_step', $step, $step);
850 my $ext = $self->ext_val;
852 ### get absolute if necessary
853 if ($base_dir !~ m
|^/|) {
854 $base_dir = $self->base_dir_abs . "/$base_dir";
857 return "$base_dir/$module/$_step.$ext";
865 return 0 if ! $self->run_hook('ready_validate', $step);
867 return $self->run_hook('validate', $step);
874 ### could do a slightly more complex test
875 return 0 if ! $ENV{REQUEST_METHOD
} || $ENV{REQUEST_METHOD
} ne 'POST';
879 sub set_ready_validate
{
882 $ENV{REQUEST_METHOD
} = ($ready) ? 'POST' : 'GET';
888 my $form = shift || $self->form;
889 my $hash = $self->run_hook('hash_validation', $step, {});
890 my $what_was_validated = [];
892 my $eob = eval { $self->vob->validate($form, $hash, $what_was_validated) };
894 die "Step $step: $@";
897 ### had an error - store the errors and return false
899 $self->add_errors($eob->as_hash({
900 as_hash_join
=> "<br>\n",
901 as_hash_suffix
=> '_error',
906 ### allow for the validation to give us some redirection
908 OUTER
: foreach my $ref (@$what_was_validated) {
909 foreach my $method (qw(append_path replace_path insert_path)) {
910 next if ! ($val = $ref->{$method});
911 $self->$method(ref $val ? @$val : $val);
919 ### allow for using ConfUtil instead of yaml
920 sub hash_validation
{
923 return $self->{hash_validation
}->{$step} ||= do {
925 my $file = $self->run_hook('file_val', $step);
927 ### allow for returning the validation hash in the filename
928 ### a scalar ref means it is a yaml document to be read by get_validation
929 if (ref($file) && ! UNIVERSAL
::isa
($file, 'SCALAR')) {
932 ### read the file - it it fails - errors should shown in the error logs
934 $hash = eval { $self->vob->get_validation($file) } || {};
940 $hash; # return of the do
945 my ($self, $step) = @_;
946 return $self->{hash_base
} ||= {
947 script_name
=> $ENV{'SCRIPT_NAME'} || $0,
948 path_info
=> $ENV{'PATH_INFO'} || '',
949 js_validation
=> sub { $self->run_hook('js_validation', $step, shift) },
950 form_name
=> sub { $self->run_hook('form_name', $step) },
954 sub hash_common
{ shift-
>{'hash_common'} ||= {} }
955 sub hash_form
{ shift-
>form }
956 sub hash_fill
{ shift-
>{'hash_fill'} ||= {} }
957 sub hash_swap
{ shift-
>{'hash_swap'} ||= {} }
958 sub hash_errors
{ shift-
>{'hash_errors'} ||= {} }
962 my $hash = $self->hash_errors;
963 my $args = ref($_[0]) ? shift : {@_};
964 foreach my $key (keys %$args) {
965 my $_key = ($key =~ /error$/) ? $key : "${key}_error";
966 if ($hash->{$_key}) {
967 $hash->{$_key} .= '<br>' . $args->{$key};
969 $hash->{$_key} = $args->{$key};
972 $hash->{'has_errors'} = 1;
975 sub add_to_errors
{ shift-
>add_errors(@_) }
976 sub add_to_swap
{ my $self = shift; $self->add_to_hash($self->hash_swap, @_) }
977 sub add_to_fill
{ my $self = shift; $self->add_to_hash($self->hash_fill, @_) }
978 sub add_to_form
{ my $self = shift; $self->add_to_hash($self->hash_form, @_) }
979 sub add_to_common
{ my $self = shift; $self->add_to_hash($self->hash_common, @_) }
980 sub add_to_base
{ my $self = shift; $self->add_to_hash($self->hash_base, @_) }
986 $new = {$new, @_} if ! ref $new; # non-hashref
987 $old->{$_} = $new->{$_} foreach keys %$new;
990 ###----------------------------------------------------------------###
992 sub forbidden_info_complete
{ 0 }
994 sub forbidden_file_print
{
996 my $step = $self->stash->{'forbidden_step'};
997 my $str = "You do not have access to \"$step\"";
1001 ###----------------------------------------------------------------###
1009 CGI::Ex::App - Full featured (within reason) application builder.
1013 Fill in the blanks and get a ready made CGI. This module is somewhat
1014 similar in spirit to CGI::Application, CGI::Path, and CGI::Builder and any
1015 other "CGI framework." As with the others, CGI::Ex::App tries to do as
1016 much as possible, in a simple manner, without getting in the
1017 developer's way. Your milage may vary.
1021 More examples will come with time. Here are the basics for now.
1026 # OR you could do the following which cleans
1027 # circular references - useful for a mod_perl situation
1028 # MyApp->navigate->cleanup;
1033 use base qw(CGI::Ex::App);
1034 use CGI::Ex::Dump qw(debug);
1036 sub valid_steps { return {success => 1, js => 1} }
1037 # default_step (main) is a valid path
1038 # note the inclusion of js step to allow the
1039 # javascript scripts in js_validation to function properly.
1041 # base_dir_abs is only needed if default print is used
1042 # template toolkit needs an INCLUDE_PATH
1043 sub base_dir_abs { '/tmp' }
1045 sub main_file_print {
1046 # reference to string means ref to content
1047 # non-reference means filename
1048 return \ "<h1>Main Step</h1>
1049 <form method=post name=[% form_name %]>
1050 <input type=text name=foo>
1051 <span style='color:red' id=foo_error>[% foo_error %]</span><br>
1055 <a href='[% script_name %]?step=foo'>Link to forbidden step</a>
1060 debug shift->history;
1061 } # show what happened
1064 # reference to string means ref to yaml document
1065 # non-reference means filename
1070 match: 'm/^([a-z]\\d)+[a-z]?\$/'
1071 match_error: Characters must alternate letter digit letter.
1078 debug $self->form, "Do something useful with form here";
1080 ### add success step
1081 $self->add_to_swap({success_msg => "We did something"});
1082 $self->append_path('success');
1083 $self->set_ready_validate(0);
1087 sub success_file_print {
1088 \ "<h1>Success Step</h1> All done.<br>
1089 ([% success_msg %])<br>
1093 ### not necessary - this is the default hash_base
1094 sub hash_base { # used to include js_validation
1095 my ($self, $step) = @_;
1096 return $self->{hash_base} ||= {
1097 script_name => $ENV{SCRIPT_NAME} || '',
1098 js_validation => sub { $self->run_hook('js_validation', $step) },
1099 form_name => sub { $self->run_hook('form_name', $step) },
1105 Note: This example would be considerably shorter if the html file
1106 (file_print) and the validation file (file_val) had been placed in
1107 separate files. Though CGI::Ex::App will work "out of the box" as
1108 shown it is more probable that any platform using it will customize
1109 the various hooks to their own tastes (for example, switching print to
1110 use a system other than Template::Toolkit).
1112 =head1 HOOKS / METHODS
1114 CGI::Ex::App works on the principles of hooks which are essentially
1115 glorified method lookups. When a hook is called, CGI::Ex::App will
1116 look for a corresponding method call for that hook for the current
1117 step name. See the discussion under the method named "hook" for more
1118 details. The methods listed below are normal method calls.
1119 Hooks and methods are looked for in the following order:
1123 =item Method C<-E<gt>new>
1125 Object creator. Takes a hash or hashref.
1127 =item Method C<-E<gt>init>
1129 Called by the default new method. Allows for any object
1132 =item Method C<-E<gt>form>
1134 Returns a hashref of the items passed to the CGI. Returns
1135 $self->{form}. Defaults to CGI::Ex::get_form.
1137 =item Method C<-E<gt>navigate>
1139 Takes a class name or a CGI::Ex::App object as arguments. If a class
1140 name is given it will instantiate an object by that class. All returns
1141 from navigate will return the object.
1143 The method navigate is essentially a safe wrapper around the ->nav_loop
1144 method. It will catch any dies and pass them to ->handle_error.
1146 =item Method C<-E<gt>nav_loop>
1148 This is the main loop runner. It figures out the current path
1149 and runs all of the appropriate hooks for each step of the path. If
1150 nav_loop runs out of steps to run (which happens if no path is set, or if
1151 all other steps run successfully), it will insert the ->default_step into
1152 the path and run nav_loop again (recursively). This way a step is always
1153 assured to run. There is a method ->recurse_limit (default 15) that
1154 will catch logic errors (such as inadvertently running the same
1155 step over and over and over).
1157 The basic outline of navigation is as follows (the default actions for hooks
1166 # dying errors will run the ->handle_error method
1171 ->path (get the path steps)
1173 # look in $ENV{'PATH_INFO'}
1174 # look in ->form for ->step_key
1177 # navigation stops if true
1179 ->valid_steps (get list of valid paths)
1181 foreach step of path {
1183 # check that path is valid
1187 # check ->allow_morph
1188 # check ->allow_nested_morph
1189 # ->morph_package (hook - get the package to bless into)
1190 # ->fixup_after_morph if morph_package exists
1196 # ->fixup_before_unmorph if blessed to previous package
1198 # exit loop if ->run_step returned true (intercepted)
1200 } end of step foreach
1203 # navigation stops if true
1205 ->default_step (inserted into path at current location)
1206 ->nav_loop (called again recursively)
1213 # exits nav_loop if true
1216 # skips this step if true (stays in nav_loop)
1218 ->prepare (hook - defaults to true)
1220 ->info_complete (hook - ran if prepare was true)
1222 # ->ready_validate (hook)
1223 # return false if ! ready_validate
1225 # ->hash_validation (hook)
1226 # ->file_val (hook - uses base_dir_rel, name_module, name_step, ext_val)
1227 # uses CGI::Ex::Validate to validate the hash
1228 # returns true if validate is true
1230 ->finalize (hook - defaults to true - ran if prepare and info_complete were true)
1232 if ! ->prepare || ! ->info_complete || ! ->finalize {
1235 # ->hash_base (hook)
1236 # ->hash_common (hook)
1237 # ->hash_form (hook)
1238 # ->hash_fill (hook)
1239 # ->hash_swap (hook)
1240 # ->hash_errors (hook)
1241 # merge form, base, common, and fill into merged fill
1242 # merge form, base, common, swap, and errors into merged swap
1243 # ->print (hook - passed current step, merged swap hash, and merged fill)
1245 # ->file_print (hook - uses base_dir_rel, name_module, name_step, ext_print)
1247 # Processes the file with Template Toolkit
1248 # Fills the any forms with CGI::Ex::Fill
1249 # Prints headers and the content
1251 ->post_print (hook - used for anything after the print process)
1253 # return true to exit from nav_loop
1257 # exits nav_loop if true
1262 =item Method C<-E<gt>pre_navigate>
1264 Called from within navigate. Called before the nav_loop method is started.
1265 If a true value is returned then navigation is skipped (the nav_loop is never
1268 =item Method C<-E<gt>post_navigate>
1270 Called from within navigate. Called after the nav_loop has finished running.
1271 Will only run if there were no errors which died during the nav_loop
1274 =item Method C<-E<gt>handle_error>
1276 If anything dies during execution, handle_error will be called with
1277 the error that had happened. Default is to debug the error and path
1280 =item Method C<-E<gt>history>
1282 Returns an arrayref of which hooks of which steps of the path were ran.
1283 Useful for seeing what happened. In general - each line of the history
1284 will show the current step, the hook requested, and which hook was
1285 actually called. (hooks that don't find a method don't add to history)
1287 =item Method C<-E<gt>path>
1289 Return an arrayref (modifyable) of the steps in the path. For each
1290 step the remaining hooks can be run. Hook methods are looked up and
1291 ran using the method "run_hook" which uses the method "hook" to lookup
1292 the hook. A history of ran hooks is stored in the array ref returned
1293 by $self->history. Default will be a single step path looked up in
1294 $form->{path} or in $ENV{PATH_INFO}. By default, path will look for
1295 $ENV{'PATH_INFO'} or the value of the form by the key step_key. For
1296 the best functionality, the arrayref returned should be the same
1297 reference returned for every call to path - this ensures that other
1298 methods can add to the path (and will most likely break if the
1299 arrayref is not the same). If navigation runs out of steps to run,
1300 the default step found in default_step will be run.
1302 =item Method C<-E<gt>default_step>
1304 Step to show if the path runs out of steps. Default value is the
1305 'default_step' property or the value 'main'.
1307 =item Method C<-E<gt>step_key>
1309 Used by default to determine which step to put in the path. The
1310 default path will only have one step within it
1312 =item Method C<-E<gt>set_path>
1314 Arguments are the steps to set. Should be called before navigation
1315 begins. This will set the path arrayref to the passed steps.
1317 =item Method C<-E<gt>append_path>
1319 Arguments are the steps to append. Can be called any time. Adds more
1320 steps to the end of the current path.
1322 =item Method C<-E<gt>replace_path>
1324 Arguments are the steps used to replace. Can be called any time.
1325 Replaces the remaining steps (if any) of the current path.
1327 =item Method C<-E<gt>insert_path>
1329 Arguments are the steps to insert. Can be called any time. Inserts
1330 the new steps at the current path location.
1332 =item Method C<-E<gt>jump>
1334 This method should not normally be used. It provides for moving to the
1335 next step at any point during the nav_loop. It effectively short circuits
1336 the remaining hooks for the current step. It does increment the recursion
1337 counter (which has a limit of ->recurse_limit - default 15). It is normally
1338 better to allow the other hooks in the loop to carry on their normal functions
1339 and avoid jumping. (Essentially, this hook behaves like a goto method to
1340 bypass everything else and continue at a different location in the path - there
1341 are times when it is necessary or useful - but most of the time should be
1344 Jump takes a single argument which is the location in the path to jump
1345 to. This argument may be either a step name, the special words
1346 "FIRST, LAST, CURRENT, PREVIOUS, OR NEXT" or the number of steps to
1347 jump forward (or backward) in the path. The default value, 1,
1348 indicates that CGI::Ex::App should jump to the next step (the default action for
1349 jump). A value of 0 would repeat the current step (watch out for
1350 recursion). A value of -1 would jump to the previous step. The
1351 special value of "LAST" will jump to the last step. The special value
1352 of "FIRST" will jump back to the first step. In each of these cases,
1353 the path array retured by ->path is modified to allow for the jumping.
1355 ### goto previous step
1356 $self->jump($self->previous_step);
1357 $self->jump('PREVIOUS');
1361 $self->jump($self->next_step);
1362 $self->jump('NEXT');
1366 ### goto current step (repeat)
1367 $self->jump($self->current_step);
1368 $self->jump('CURRENT');
1372 $self->jump($self->last_step);
1373 $self->jump('LAST');
1376 $self->jump($self->first_step);
1377 $self->jump('FIRST');
1379 =item Method C<-E<gt>exit_nav_loop>
1381 This method should not normally used. It allows for a long jump to the
1382 end of all nav_loops (even if they are recursively nested). This
1383 effectively short circuits all remaining hooks for the current and
1384 remaining steps. It is used to allow the ->jump functionality. If the
1385 application has morphed, it will be unmorphed before returning.
1387 =item Method C<-E<gt>recurse_limit>
1389 Default 15. Maximum number of times to allow nav_loop to call itself.
1390 If ->jump is used alot - the recurse_limit will be reached more quickly.
1391 It is safe to raise this as high as is necessary - so long as it is intentional.
1393 =item Method C<-E<gt>valid_steps>
1395 Returns a hashref of path steps that are allowed. If step found in
1396 default method path is not in the hash, the method path will return a
1397 single step "forbidden" and run its hooks. If no hash or undef is
1398 returned, all paths are allowed (default). A key "forbidden_step"
1399 containing the step that was not valid will be placed in the stash.
1400 Often the valid_steps method does not need to be defined as arbitrary
1401 method calls are not possible with CGI::Ex::App.
1403 =item Method C<-E<gt>previous_step, -E<gt>current_step, -E<gt>next_step, -E<gt>last_step, -E<gt>first_step>
1405 Return the previous, current, next, last, and first step name - useful for figuring
1406 out where you are in the path. Note that first_step may not be the same
1407 thing as default_step if the path was overridden.
1409 =item Method C<-E<gt>pre_loop>
1411 Called right before the navigation loop is started. At this point the
1412 path is set (but could be modified). The only argument is a reference
1413 to the path array. If it returns a true value - the navigation
1416 =item Method C<-E<gt>run_hook>
1418 Calls "hook" to get a code ref which it then calls and returns the
1419 result. Arguments are the same as that for "hook".
1421 =item Method C<-E<gt>hook>
1423 Arguments are a hook name, a pathstep name, and an optional code sub
1424 or default value (default value will be turned to a sub) (code sub
1425 will be called as method of $self).
1427 my $code = $self->hook('main', 'info_complete', sub {return 0});
1428 ### will look first for $self->main_info_complete;
1429 ### will then look for $self->info_complete;
1430 ### will then run $self->$default_passed_sub; # sub {return 0}
1432 This system is used to allow for multiple steps to be in the same
1433 file and still allow for moving some steps out to external sub classed
1434 packages. If the application has successfully morphed then it is not
1435 necessary to add the step name to the beginning of the method name as
1436 the morphed packages method will override the base package (it is still
1437 OK to use the full method name "${step}_hookname").
1439 If a hook is found (or a default value is found) then an entry is added
1440 to the arrayref contained in ->history.
1442 =item Method C<-E<gt>morph>
1444 Allows for temporarily "becoming" another object type for the
1445 execution of the current step. This allows for separating some steps
1446 out into their own packages. Morph will only run if the method
1447 allow_morph returns true. Additionally if the allow_morph returns a hash
1448 ref, morph will only run if the step being morphed to is in the hash.
1449 The morph call occurs at the beginning of the step loop. A
1450 corresponding unmorph call occurs before the loop is exited. An
1451 object can morph several levels deep if allow_nested_morph returns
1452 true. For example, an object running as Foo::Bar that is looping on
1453 the step "my_step" that has allow_morph = 1, will do the following:
1454 call the hook morph_package (which would default to returning
1455 Foo::Bar::MyStep in this case), translate this to a package filename
1456 (Foo/Bar/MyStep.pm) and try and require it, if the file can be
1457 required, the object is blessed into that package. If that package
1458 has a "fixup_after_morph" method, it is called. The navigate loop
1459 then continues for the current step. At any exit point of the loop,
1460 the unmorph call is made which reblesses the object into the original
1463 It is possible to call morph earlier on in the program. An example of
1464 a useful early use of morph would be as in the following code:
1466 sub allow_morph { 1 }
1470 if ($ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} =~ s|^/(\w+)||) {
1472 $self->morph($step);
1473 $ENV{'PATH_INFO'} = "/$step";
1474 $self->stash->{'base_morphed'} = 1;
1481 $self->unmorph if $self->stash->{'base_morphed'};
1484 If this code was in a module Base.pm and the cgi running was cgi/base
1488 # OR - for mod_perl resident programs
1489 Base->navigate->cleanup;
1491 sub post_navigate { shift->cleanup }
1493 and you created a sub module that inherited Base.pm called
1494 Base/Ball.pm -- you could then access it using cgi/base/ball. You
1495 would be able to pass it steps using either cgi/base/ball/step_name or
1496 cgi/base/ball?step=step_name - Or Base/Ball.pm could implement its
1497 own path. It should be noted that if you do an early morph, it is
1498 suggested to provide a call to unmorph. And if you want to let your
1499 early morphed object morph again - you will need to provide
1501 sub allow_nested_morph { 1 }
1503 With allow_nested_morph enabled you could create the file
1504 Base/Ball/StepName.pm which inherits Base/Ball.pm. The Base.pm, with
1505 the custom init and default path method, would automatically morph us
1506 first into a Base::Ball object (during init) and then into a
1507 Base::Ball::StepName object (during the navigation loop).
1509 =item Method C<-E<gt>unmorph>
1511 Allows for returning an object back to its previous blessed state.
1512 This only happens if the object was previously morphed into another
1513 object type. Before the object is reblessed the method
1514 "fixup_before_unmorph" is called if it exists.
1516 =item Method C<-E<gt>allow_morph>
1518 Boolean value. Specifies whether or not morphing is allowed.
1519 Defaults to the property "allow_morph" if found, otherwise false.
1520 For more granularity, if true value is a hash, the step being
1521 morphed to must be in the hash.
1523 =item Method C<-E<gt>allow_nested_morph>
1525 Boolean value. Specifies whether or not nested morphing is allowed.
1526 Defaults to the property "allow_nested_morph" if found, otherwise
1527 false. For more granularity, if true value is a hash, the step being
1528 morphed to must be in the hash.
1530 =item Hook C<-E<gt>morph_package>
1532 Used by morph. Return the package name to morph into during a morph
1533 call. Defaults to using the current object type as a base. For
1534 example, if the current object running is a Foo::Bar object and the
1535 step running is my_step, then morph_package will return
1538 =item Hook C<-E<gt>run_step>
1540 Runs all of the hooks specific to each step, beginning with pre_step
1541 and ending with post_step. Called after ->morph($step) has been
1542 run. If this returns true, the nav_loop is exited (meaning the
1543 run_step hook displayed the information). If it returns false,
1544 the nav_loop continues on to run the next step. This is essentially
1545 the same thing as a method defined in CGI::Applications ->run_modes.
1547 =item Hook C<-E<gt>pre_step>
1549 Ran at the beginning of the loop before prepare, info_compelete, and
1550 finalize are called. If it returns true, execution of nav_loop is
1551 returned and no more steps are processed.
1553 =item Hook C<-E<gt>skip>
1555 Ran at the beginning of the loop before prepare, info_compelete, and
1556 finalize are called. If it returns true, nav_loop moves on to the
1557 next step (the current step is skipped).
1559 =item Hook C<-E<gt>prepare>
1561 Defaults to true. A hook before checking if the info_complete is true.
1563 =item Hook C<-E<gt>info_complete>
1565 Checks to see if all the necessary form elements have been passed in.
1566 Calls hooks ready_validate, and validate. Will not be run unless
1567 prepare returns true (default).
1569 =item Hook C<-E<gt>finalize>
1571 Defaults to true. Used to do whatever needs to be done with the data once
1572 prepare has returned true and info_complete has returned true. On failure
1573 the print operations are ran. On success navigation moves on to the next
1576 =item Hook C<-E<gt>ready_validate>
1578 Should return true if enough information is present to run validate.
1579 Default is to look if $ENV{'REQUEST_METHOD'} is 'POST'. A common
1580 usage is to pass a common flag in the form such as 'processing' => 1
1581 and check for its presence - such as the following:
1583 sub ready_validate { shift->form->{'processing'} }
1585 =item Method C<-E<gt>set_ready_validate>
1587 Sets that the validation is ready to validate. Should set the value
1588 checked by the hook ready_validate. The following would complement the
1589 processing flag above:
1591 sub set_ready_validate {
1594 $self->form->{'processing'} = 1;
1596 delete $self->form->{'processing'};
1600 Note thate for this example the form key "processing" was deleted. This
1601 is so that the call to fill in any html forms won't swap in a value of
1602 zero for form elements named "processing."
1604 =item Hook C<-E<gt>validate>
1606 Runs validation on the information posted in $self->form. Uses
1607 CGI::Ex::Validate for the validation. Calls the hook hash_validation
1608 to load validation information. Should return true if enough
1609 information is present to run validate. Errors are stored as a hash
1610 in $self->{hash_errors} via method add_errors and can be checked for
1611 at a later time with method has_errors (if the default validate was
1614 Upon success, it will look through all of the items which
1615 were validated, if any of them contain the keys append_path, insert_path,
1616 or replace_path, that method will be called with the value as arguments.
1617 This allows for the validation to apply redirection to the path. A
1620 {field => 'foo', required => 1, append_path => ['bar', 'baz']}
1622 would append 'bar' and 'baz' to the path should all validation succeed.
1624 =item Hook C<-E<gt>hash_validation>
1626 Returns a hash of the validation information to check form against.
1627 By default, will look for a filename using the hook file_val and will
1628 pass it to CGI::Ex::Validate::get_validation. If no file_val is
1629 returned or if the get_validation fails, an empty hash will be returned.
1630 Validation is implemented by ->vob which loads a CGI::Ex::Validate object.
1632 =item Hook C<-E<gt>file_val>
1634 Returns a filename containing the validation. Adds method
1635 base_dir_rel to hook name_module, and name_step and adds on the
1636 default file extension found in $self->ext_val which defaults to the
1637 global $EXT_VAL (the property $self->{ext_val} may also be set). File
1638 should be readible by CGI::Ex::Validate::get_validation.
1640 =item Hook C<-E<gt>js_validation>
1643 Will return Javascript that is capable of validating the form. This
1644 is done using the capabilities of CGI::Ex::Validate. This will call
1645 the hook hash_validation which will then be encoded into yaml and
1646 placed in a javascript string. It will also call the hook form_name
1647 to determine which html form to attach the validation to. The method
1648 js_uri_path is called to determine the path to the appropriate
1649 yaml_load.js and validate.js files. If the method ext_val is htm,
1650 then js_validation will return an empty string as it assumes the htm
1651 file will take care of the validation itself. In order to make use
1652 of js_validation, it must be added to either the hash_base, hash_common, hash_swap or
1653 hash_form hook (see examples of hash_base used in this doc).
1655 =item Hook C<-E<gt>form_name>
1657 Return the name of the form to attach the js validation to. Used by
1660 =item Method C<-E<gt>js_uri_path>
1662 Return the URI path where the CGI/Ex/yaml_load.js and
1663 CGI/Ex/validate.js files can be found. This will default to
1664 "$ENV{SCRIPT_NAME}/js" if the path method has not been overridden,
1665 otherwise it will default to "$ENV{SCRIPT_NAME}?step=js&js=" (the
1666 latter is more friendly with overridden paths). A default handler for
1667 the "js" step has been provided in "js_run_step" (this handler will
1668 nicely print out the javascript found in the js files which are
1669 included with this distribution - if valid_steps is defined, it must
1670 include the step "js" - js_run_step will work properly with the
1671 default "path" handler.
1673 =item Hook C<-E<gt>hash_swap>
1675 Called in preparation for print after failed prepare, info_complete,
1676 or finalize. Should contain a hash of any items needed to be swapped
1677 into the html during print. Will be merged with hash_base, hash_common, hash_form,
1678 and hash_errors. Can be populated by passing a hash to ->add_to_swap.
1680 =item Hook C<-E<gt>hash_form>
1682 Called in preparation for print after failed prepare, info_complete,
1683 or finalize. Defaults to ->form. Can be populated by passing a hash
1686 =item Hook C<-E<gt>hash_fill>
1688 Called in preparation for print after failed prepare, info_complete,
1689 or finalize. Should contain a hash of any items needed to be filled
1690 into the html form during print. Items from hash_form, hash_base, and hash_common
1691 will be layered on top during a print cycle. Can be populated by passing
1692 a hash to ->add_to_fill.
1694 By default - forms are sticky and data from previous requests will
1695 try and populate the form. There is a method called ->no_fill which
1696 will turn off sticky forms.
1698 =item Method C<-E<gt>no_fill>
1700 Passed the current step. Should return boolean value of whether or not
1701 to fill in the form on the printed page. (prevents sticky forms)
1703 =item Hook C<-E<gt>hash_errors>
1705 Called in preparation for print after failed prepare, info_complete,
1706 or finalize. Should contain a hash of any errors that occured. Will
1707 be merged into hash_form before the pass to print. Eash error that
1708 occured will be passed to method format_error before being added to
1709 the hash. If an error has occurred, the default validate will
1710 automatically add {has_errors =>1}. To the error hash at the time of
1711 validation. has_errors will also be added during the merge incase the
1712 default validate was not used. Can be populated by passing a hash to
1713 ->add_to_errors or ->add_errors.
1715 =item Hook C<-E<gt>hash_common>
1717 Almost identical in function and purpose to hash_base. It is
1718 intended that hash_base be used for common items used in various
1719 scripts inheriting from a common CGI::Ex::App type parent. Hash_common
1720 is more intended for step level populating of both swap and fill.
1722 =item Hook C<-E<gt>hash_base>
1724 A hash of base items to be merged with hash_form - such as pulldown
1725 menues. It will now also be merged with hash_fill, so it can contain
1726 default fillins. Can be populated by passing a hash to ->add_to_base.
1727 By default the following sub is what is used for hash_common (or something
1728 similiar). Note the use of values that are code refs - so that the
1729 js_validation and form_name hooks are only called if requested:
1732 my ($self, $step) = @_;
1733 return $self->{hash_base} ||= {
1734 script_name => $ENV{SCRIPT_NAME},
1735 js_validation => sub { $self->run_hook('js_validation', $step) },
1736 form_name => sub { $self->run_hook('form_name', $step) },
1740 =item Hook C<-E<gt>name_module>
1742 Return the name (relative path) that should be prepended to name_step
1743 during the default file_print and file_val lookups. Defaults to
1746 =item Hook C<-E<gt>name_step>
1748 Return the step (appended to name_module) that should used when
1749 looking up the file in file_print and file_val lookups. Defaults to
1752 =item Hook C<-E<gt>file_print>
1754 Returns a filename of the content to be used in the default print
1755 hook. Adds method base_dir_rel to hook name_module, and name_step and
1756 adds on the default file extension found in $self->ext_print which
1757 defaults to the global $EXT_PRINT (the property $self->{ext_print} may
1758 also be set). Should be a file that can be handled by hook print.
1760 =item Hook C<-E<gt>print>
1762 Take the information generated by prepared_print, format it, and print it out.
1763 Default incarnation uses Template::Toolkit. Arguments are: step name, form hashref,
1766 =item Hook C<-E<gt>prepared_print>
1768 Called when any of prepare, info_complete, or finalize fail. Prepares
1769 a form hash and a fill hash to pass to print. The form hash is primarily
1770 intended for use by the templating system. The fill hash is intended
1771 to be used to fill in any html forms.
1773 =item Hook C<-E<gt>post_print>
1775 A hook which occurs after the printing has taken place. Is only run
1776 if the information was not complete. Useful for printing rows of a
1779 =item Hook C<-E<gt>post_step>
1781 Ran at the end of the step's loop if prepare, info_complete, and
1782 finalize all returned true. Allows for cleanup. If a true value is
1783 returned, execution of navigate is returned and no more steps are
1786 =item Method C<-E<gt>post_loop>
1788 Ran after all of the steps in the loop have been processed (if
1789 prepare, info_complete, and finalize were true for each of the steps).
1790 If it returns a true value the navigation loop will be aborted. If it
1791 does not return true, navigation continues by then inserting the step
1792 $self->default_step and running $self->nav_loop again (recurses) to
1793 fall back to the default step.
1795 =item Method C<-E<gt>stash>
1797 Returns a hashref that can store arbitrary user space data without
1798 clobering the internals of the application.
1800 =item Method C<-E<gt>add_property>
1802 Takes the property name as an argument. Creates an accessor that can
1803 be used to access a new property. If there were additional arguments
1804 they will call the new accessor. Calling the new accessor with an
1805 argument will set the property. Using the accessor in an assignment
1806 will also set the property (it is an lvalue). Calling the accessor in
1807 any other way will return the value.
1809 =item Method C<-E<gt>cleanup>
1811 Can be used at the end of execution to tear down the structure.
1812 Default method starts a cleanup_cross_references call.
1814 =item Method C<-E<gt>cleanup_cross_references>
1816 Used to destroy links in nested structures. Will spider through the
1817 data structure of the passed object and remove any blessed objects
1818 that are no weakly referenced. This means if you have a reference to
1819 an object in a global cache, that object should have its reference
1820 weakened in the global cache. Requires Scalar::Util to function. Use
1821 of this function is highly recommended in mod_perl environments to
1822 make sure that there are no dangling objects in memory. There are
1823 some global caches that can't be fixed (such as Template::Parser's
1824 reference to Template::Grammar in the Template::Toolkit). For these
1825 situations there is a %CLEANUP_EXCLUDE hash that contains the names of
1826 Object types to exclude from the cleanup process. Add any such global
1827 hashes (or objects with references to the global hashes) there.
1831 =head1 OTHER APPLICATION MODULES
1833 The concepts used in CGI::Ex::App are not novel or unique. However, they
1834 are all commonly used and very useful. All application builders were
1835 built because somebody observed that there are common design patterns
1836 in CGI building. CGI::Ex::App differs in that it has found more common design
1839 CGI::Ex::App is intended to be sub classed, and sub sub classed, and each step
1840 can choose to be sub classed or not. CGI::Ex::App tries to remain simple
1841 while still providing "more than one way to do it." It also tries to avoid
1842 making any sub classes have to call ->SUPER::.
1844 There are certainly other modules for building CGI applications. The
1845 following is a short list of other modules and how CGI::Ex::App is
1850 =item C<CGI::Application>
1852 Seemingly the most well know of application builders.
1853 CGI::Ex::App is different in that it:
1855 * Uses Template::Toolkit by default
1856 CGI::Ex::App can easily use another toolkit by simply
1857 overriding the ->print method.
1858 CGI::Application uses HTML::Template.
1859 * Offers integrated data validation.
1860 CGI::Application has had custom addons created that
1861 add some of this functionality. CGI::Ex::App has the benefit
1862 that once validation is created,
1863 * Allows the user to print at any time (so long as proper headers
1864 are sent. CGI::Application requires data to be pipelined.
1865 * Offers hooks into the various phases of each step ("mode" in
1866 CGI::Application lingo). CGI::Application essentially
1868 * Support for easily jumping around in navigation steps.
1869 * Support for storing some steps in another package.
1871 CGI::Ex::App and CGI::Application are similar in that they take care
1872 of handling headers and they allow for calling other "runmodes" from
1873 within any given runmode. CGI::Ex::App's ->run_step is essentially
1874 equivalent to a method call defined in CGI::Application's ->run_modes.
1875 The ->run method of CGI::Application starts the application in the same
1876 manner as CGI::Ex::App's ->navigate call. Many of the hooks around
1877 CGI::Ex::App's ->run_step call are similar in nature to those provided by
1880 =item C<CGI::Prototype>
1882 There are actually many simularities. One of the nicest things about
1883 CGI::Prototype is that it is extremely short (very very short). The
1884 ->activate starts the application in the same manner as CGI::Ex::App's
1885 =>navigate call. Both use Template::Tookit as the default template system.
1886 CGI::Ex::App is differrent in that it:
1888 * Offers integrated data validation.
1889 CGI::Application has had custom addons created that
1890 add some of this functionality. CGI::Ex::App has the benefit
1891 that once validation is created,
1892 * Offers more hooks into the various phases of each step.
1893 * Support for easily jumping around in navigation steps.
1894 * Support for storing some steps in another package.
1898 CGI::Path and CGI::Ex::App are fairly similar in may ways as they
1899 were created under similar lines of thought. The primary difference
1900 in these two is that CGI::Ex::App:
1902 * Does not provide "automated path following" based on
1903 validated key information. CGI::Path works well for
1904 wizard based applications. CGI::Ex::App assumes that
1905 the application will chose it's own path (it works very
1906 well in non-linear paths - it also works fine in
1907 linear paths but it doesn't provide some of magic that
1908 CGI::Path provides).
1909 * Does not provide integrated session support. CGI::Path
1910 requires it for path navigation. CGI::Ex::App assumes that
1911 if session support or authentication is needed by an
1912 application, a custom Application layer that inherits
1913 from CGI::Ex::App will be written to provide this support.
1914 * Offers more granularity in the navigation phase. CGI::Path
1915 has successfully been used as a sub class of CGI::Ex::App
1916 with limited modifications.
1922 Uses CGI::Ex for header support by default - which means that support
1923 for mod_perl 2 is limited at this point.
1925 There are a lot of hooks. Actually this is not a bug. Some may
1926 prefer not calling as many hooks - they just need to override
1927 methods high in the chain and subsequent hooks will not be called.
1931 Bizhosting.com - giving a problem that fit basic design patterns.
1932 Earl Cahill - pushing the idea of more generic frameworks.
1933 Adam Erickson - design feedback, bugfixing, feature suggestions.
1934 James Lance - design feedback, bugfixing, feature suggestions.
This page took 0.165992 seconds and 4 git commands to generate.