]> Dogcows Code - chaz/p5-CGI-Ex/commitdiff
CGI::Ex 2.08 v2.08
authorPaul Seamons <perl@seamons.com>
Fri, 2 Mar 2007 00:00:00 +0000 (00:00 +0000)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Fri, 9 May 2014 23:46:41 +0000 (17:46 -0600)
21 files changed:
Changes
META.yml
lib/CGI/Ex.pm
lib/CGI/Ex/App.pm
lib/CGI/Ex/App.pod
lib/CGI/Ex/Auth.pm
lib/CGI/Ex/Conf.pm
lib/CGI/Ex/Die.pm
lib/CGI/Ex/Dump.pm
lib/CGI/Ex/Fill.pm
lib/CGI/Ex/JSONDump.pm
lib/CGI/Ex/Template.pm
lib/CGI/Ex/Template.pod
lib/CGI/Ex/Validate.pm
lib/CGI/Ex/validate.js
samples/js_validate_1.html
samples/js_validate_2.html
samples/js_validate_3.html
t/4_app_00_base.t
t/7_template_00_base.t
t/9_jsondump_00_base.t

diff --git a/Changes b/Changes
index 194f1142a40312cc8ae2f21c2ee7e9d31bf6a350..e42fa35ead6b37ed24067bfc38d1ff0b4cf40417 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,14 @@
+2.08   2007-03-02
+        * Allow for digits passed to %*s patterns in format, and fmt or CGI::Ex::Template
+        * Fix bug in validate.js to allow for non-existant fields (with replace)
+        * Warn clean in Validate.pm for non-existant field (with replace)
+        * Fix bug in validate.js with trim_control_chars - was broken entirely
+        * Add no_tag_splitting flag to JSONDump
+        * Allow for more types of numbers to be quoted in JSONDump
+        * Add path_info_map_base method to CGI::Ex::App
+        * Add path_info_map hook to CGI::Ex::App
+        * Updated and added much documentation to CGI::Ex::App
+
 2.07   2007-01-30
         * Add clear_app method which flushes items pertaining to navigation.
         * Allow for CGI::Ex::Template PLUGIN_BASE to be a scalar OR an arrayref.
 2.07   2007-01-30
         * Add clear_app method which flushes items pertaining to navigation.
         * Allow for CGI::Ex::Template PLUGIN_BASE to be a scalar OR an arrayref.
index 05522c4513c41a2c4242799e0d94d1f0c9765ad2..e16c22fb7e68926062452a1abeb9cfa9523e507a 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -1,7 +1,7 @@
 # http://module-build.sourceforge.net/META-spec.html
 #XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
 name:         CGI-Ex
 # http://module-build.sourceforge.net/META-spec.html
 #XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
 name:         CGI-Ex
-version:      2.07
+version:      2.08
 version_from: lib/CGI/Ex.pm
 installdirs:  site
 requires:
 version_from: lib/CGI/Ex.pm
 installdirs:  site
 requires:
index db9cda672d33cebfbf26ab996d24648ec7ea5c3f..8c5e6b07ba79d01e6264fd760ad70b7b2f900794 100644 (file)
@@ -24,7 +24,7 @@ use vars qw($VERSION
 use base qw(Exporter);
 
 BEGIN {
 use base qw(Exporter);
 
 BEGIN {
-    $VERSION               = '2.07';
+    $VERSION               = '2.08';
     $PREFERRED_CGI_MODULE  ||= 'CGI';
     @EXPORT = ();
     @EXPORT_OK = qw(get_form
     $PREFERRED_CGI_MODULE  ||= 'CGI';
     @EXPORT = ();
     @EXPORT_OK = qw(get_form
index 739c82fb755307e22a63d39f28257b1fd723639c..296285893b1e55f18783cfc7c24a5d9504215cf6 100644 (file)
@@ -10,7 +10,7 @@ use strict;
 use vars qw($VERSION);
 
 BEGIN {
 use vars qw($VERSION);
 
 BEGIN {
-    $VERSION = '2.07';
+    $VERSION = '2.08';
 
     Time::HiRes->import('time') if eval {require Time::HiRes};
     eval {require Scalar::Util};
 
     Time::HiRes->import('time') if eval {require Time::HiRes};
     eval {require Scalar::Util};
@@ -122,6 +122,19 @@ sub nav_loop {
         ### allow for becoming another package (allows for some steps in external files)
         $self->morph($step);
 
         ### allow for becoming another package (allows for some steps in external files)
         $self->morph($step);
 
+        ### allow for mapping path_info pieces to form elements
+        if (my $info = $ENV{'PATH_INFO'}) {
+            my $maps = $self->run_hook('path_info_map', $step) || [];
+            croak 'Usage: sub path_info_map { [[qr{/path_info/(\w+)}, "keyname"]] }'
+                if ! UNIVERSAL::isa($maps, 'ARRAY') || (@$maps && ! UNIVERSAL::isa($maps->[0], 'ARRAY'));
+            foreach my $map (@$maps) {
+                my @match = $info =~ $map->[0];
+                next if ! @match;
+                $self->form->{$map->[$_]} = $match[$_ - 1] foreach grep {! defined $self->form->{$map->[$_]}} 1 .. $#$map;
+                last;
+            }
+        }
+
         ### run the guts of the step
         my $handled = $self->run_hook('run_step', $step);
 
         ### run the guts of the step
         my $handled = $self->run_hook('run_step', $step);
 
@@ -182,10 +195,21 @@ sub path {
     if (! $self->{'path'}) {
         my $path = $self->{'path'} = []; # empty path
 
     if (! $self->{'path'}) {
         my $path = $self->{'path'} = []; # empty path
 
-        my $step = $self->form->{ $self->step_key };
-        $step = lc($1) if ! $step && $ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} =~ m|^/(\w+)|;
+        ### add initial items to the form hash from path_info
+        if (my $info = $ENV{'PATH_INFO'}) {
+            my $maps = $self->path_info_map_base || [];
+            croak 'Usage: sub path_info_map_base { [[qr{/path_info/(\w+)}, "keyname"]] }'
+                if ! UNIVERSAL::isa($maps, 'ARRAY') || (@$maps && ! UNIVERSAL::isa($maps->[0], 'ARRAY'));
+            foreach my $map (@$maps) {
+                my @match = $info =~ $map->[0];
+                next if ! @match;
+                $self->form->{$map->[$_]} = $match[$_ - 1] foreach grep {! defined $self->form->{$map->[$_]}} 1 .. $#$map;
+                last;
+            }
+        }
 
         ### make sure the step is valid
 
         ### make sure the step is valid
+        my $step = $self->form->{$self->step_key};
         if (defined $step) {
             if ($step =~ /^_/) {         # can't begin with _
                 $self->stash->{'forbidden_step'} = $step;
         if (defined $step) {
             if ($step =~ /^_/) {         # can't begin with _
                 $self->stash->{'forbidden_step'} = $step;
@@ -205,6 +229,11 @@ sub path {
     return $self->{'path'};
 }
 
     return $self->{'path'};
 }
 
+sub path_info_map_base {
+    my $self = shift;
+    return [[qr{/(\w+)}, $self->step_key]];
+}
+
 sub set_path {
     my $self = shift;
     my $path = $self->{'path'} ||= [];
 sub set_path {
     my $self = shift;
     my $path = $self->{'path'} ||= [];
@@ -700,6 +729,8 @@ sub clear_app {
 ###----------------------------------------------------------------###
 ### default hook implementations
 
 ###----------------------------------------------------------------###
 ### default hook implementations
 
+sub path_info_map { }
+
 sub run_step {
     my $self = shift;
     my $step = shift;
 sub run_step {
     my $self = shift;
     my $step = shift;
@@ -825,7 +856,7 @@ sub pre_step   { 0 } # success indicates we handled step (don't continue step or
 sub skip       { 0 } # success indicates to skip the step (and continue loop)
 sub prepare    { 1 } # failure means show step
 sub finalize   { 1 } # failure means show step
 sub skip       { 0 } # success indicates to skip the step (and continue loop)
 sub prepare    { 1 } # failure means show step
 sub finalize   { 1 } # failure means show step
-sub post_print { 0 } # success indicates we handled step (don't continue loop)
+sub post_print { 0 }
 sub post_step  { 0 } # success indicates we handled step (don't continue step or loop)
 
 sub name_step {
 sub post_step  { 0 } # success indicates we handled step (don't continue step or loop)
 
 sub name_step {
index e7c827d038e110c1cc0c719af8b968a0d473c1f2..f44f9602b38d1274a4875699f0efe82bbba930fe 100644 (file)
 
 CGI::Ex::App - Anti-framework application framework.
 
 
 CGI::Ex::App - Anti-framework application framework.
 
+=head1 SYNOPSIS
+
+    #!/usr/bin/perl -w
+
+    use strict;
+    use base qw(CGI::Ex::App);
+
+    __PACKAGE__->navigate;
+    exit;
+
+    sub main_file_print {
+        return \ "Hello World";
+    }
+
+There is a longer "SYNOPSIS" after the process flow discussion.
+
 =head1 DESCRIPTION
 
 Fill in the blanks and get a ready made web application.  This module
 is somewhat similar in spirit to CGI::Application, CGI::Path, and
 CGI::Builder and any other "CGI framework."  As with the others,
 CGI::Ex::App tries to do as much of the mundane things, in a simple
 =head1 DESCRIPTION
 
 Fill in the blanks and get a ready made web application.  This module
 is somewhat similar in spirit to CGI::Application, CGI::Path, and
 CGI::Builder and any other "CGI framework."  As with the others,
 CGI::Ex::App tries to do as much of the mundane things, in a simple
-manner, without getting in the developer's way.  Your mileage may vary.
+manner, without getting in the developer's way.  However, there are
+various design patterns for CGI applications that CGI::Ex::App handles
+for you that the other frameworks require you to bring in extra support.
+The entire CGI::Ex suite has been taylored to work seamlessly together.
+Your mileage in building applications may vary.
 
 If you build applications that submit user information, validate it,
 re-display it, fill in forms, or separate logic into separate modules,
 then this module may be for you.  If all you need is a dispatch
 
 If you build applications that submit user information, validate it,
 re-display it, fill in forms, or separate logic into separate modules,
 then this module may be for you.  If all you need is a dispatch
-engine, then this still may be for you.  If all want is to look at
+engine, then this still may be for you.  If all you want is to look at
 user passed information, then this may still be for you.  If you like
 writing bare metal code, this could still be for you.  If you don't want
 to write any code, this module will help - but you still need to
 user passed information, then this may still be for you.  If you like
 writing bare metal code, this could still be for you.  If you don't want
 to write any code, this module will help - but you still need to
-provide you key actions.
+provide your key actions and html.
+
+One of the great benefits of CGI::Ex::App vs. Catalyst or Rails style
+frameworks is that the model of CGI::Ex::App can be much more abstract
+as models often are.
+
+=head1 DEFAULT PROCESS FLOW
+
+The following pseudo-code describes the process flow
+of the CGI::Ex::App framework.  Several portions of the flow
+are encapsulated in hooks which may be completely overridden to give
+different flow.  All of the default actions are shown.  It may look
+like a lot to follow, but if the process is broken down into the
+discrete operations of step iteration, data validation, and template
+printing the flow feels more natural.
+
+=head2 navigate
+
+The process starts off by calling ->navigate.
+
+    navigate {
+        eval {
+            ->pre_navigate
+            ->nav_loop
+            ->post_navigate
+        }
+        # dying errors will run the ->handle_error method
+
+        ->destroy
+    }
+
+=head2 nav_loop
+
+The nav_loop method will run as follows:
+
+    nav_loop {
+        ->path (get the array of path steps)
+            # ->path_info_map_base (method - map ENV PATH_INFO to form)
+            # look in ->form for ->step_key
+            # make sure step is in ->valid_steps (if defined)
+
+        ->pre_loop($path)
+            # navigation stops if true
+
+        foreach step of path {
+
+            ->morph
+                # check ->allow_morph
+                # check ->allow_nested_morph
+                # ->morph_package (hook - get the package to bless into)
+                # ->fixup_after_morph if morph_package exists
+                # if no package is found, process continues in current file
+
+            ->path_info_map (hook - map PATH_INFO to form)
+
+            ->run_step (hook)
+
+            ->refine_path (hook)
+                # only called if run_step returned false (page not printed)
+                ->next_step (hook) # find next step and add to path
+                ->set_ready_validate(0) (hook)
+
+            ->unmorph
+                # only called if morph worked
+                # ->fixup_before_unmorph if blessed to current package
+
+            # exit loop if ->run_step returned true (page printed)
+
+        } end of foreach step
+
+        ->post_loop
+            # navigation stops if true
+
+        ->default_step
+        ->insert_path (puts the default step into the path)
+        ->nav_loop (called again recursively)
+
+    } end of nav_loop
+
+=head2 run_step (hook)
+
+For each step of the path the following methods will be run
+during the run_step hook.
+
+    run_step {
+        ->pre_step (hook)
+            # exits nav_loop if true
+
+        ->skip (hook)
+            # skips this step if true (stays in nav_loop)
+
+        ->prepare (hook - defaults to true)
+
+        ->info_complete (hook - ran if prepare was true)
+            ->ready_validate (hook)
+            return false if ! ready_validate
+            ->validate (hook - uses CGI::Ex::Validate to validate form info)
+                ->hash_validation (hook)
+                   ->file_val (hook)
+                       ->base_dir_abs
+                       ->base_dir_rel
+                       ->name_module
+                       ->name_step
+                       ->ext_val
+            returns true if validate is true or if nothing to validate
+
+        ->finalize (hook - defaults to true - ran if prepare and info_complete were true)
+
+        if ! ->prepare || ! ->info_complete || ! ->finalize {
+            ->prepared_print
+                ->hash_base (hook)
+                ->hash_common (hook)
+                ->hash_form (hook)
+                ->hash_fill (hook)
+                ->hash_swap (hook)
+                ->hash_errors (hook)
+                # merge form, base, common, and fill into merged fill
+                # merge form, base, common, swap, and errors into merged swap
+                ->print (hook - passed current step, merged swap hash, and merged fill)
+                     ->file_print (hook - uses base_dir_rel, name_module, name_step, ext_print)
+                     ->swap_template (hook - processes the file with CGI::Ex::Template)
+                          ->template_args (hook - passed to CGI::Ex::Template->new)
+                     ->fill_template (hook - fills the any forms with CGI::Ex::Fill)
+                          ->fill_args (hook - passed to CGI::Ex::Fill::fill)
+                     ->print_out (hook - print headers and the content to STDOUT)
+
+            ->post_print (hook - used for anything after the print process)
+
+            # return true to exit from nav_loop
+        }
+
+        ->post_step (hook)
+            # exits nav_loop if true
+
+    } end of run_step
+
+It is important to learn the function and placement of each of the
+hooks in the process flow in order to make the most of CGI::Ex::App.
+It is enough to begin by learning a few common hooks - such as
+hash_validation, hash_swap, and finalize, and then learn about other
+hooks as needs arise.  Sometimes, it is enough to simply override the
+run_step hook and take care of processing the entire step yourself.
+
+Because of the hook based system, and because CGI::Ex::App uses
+sensible defaults, it is very easy to override a little or a lot which
+ends up giving the developer a lot of flexibility.
+
+Consequently, it should be possible to use CGI::Ex::App with the other
+frameworks such as CGI::Application or CGI::Prototype.  For these you
+could simple let each "runmode" call the run_step hook of CGI::Ex::App
+and you will instantly get all of the common process flow for free.
+
+=head1 MAPPING URI TO STEP
+
+The default out of the box configuration will map URIs to steps as follows:
+
+    # Assuming /cgi-bin/my_cgi is the program being run
+
+    URI:  /cgi-bin/my_cgi
+    STEP: main
+    FORM: {}
+    WHY:  No other information is passed.  The path method is
+          called which eventually calls ->default_step which
+          defaults to "main"
+
+    URI:  /cgi-bin/my_cgi?foo=bar
+    STEP: main
+    FORM: {foo => "bar"}
+    WHY:  Same as previous example except that QUERY_STRING
+          information was passed and placed in form.
+
+    URI:  /cgi-bin/my_cgi?step=my_step
+    STEP: my_step
+    FORM: {step => "my_step"}
+    WHY:  The path method is called which looks in $self->form
+          for the key ->step_key (which defaults to "step").
+
+    URI:  /cgi-bin/my_cgi?step=my_step&foo=bar
+    STEP: my_step
+    FORM: {foo => "bar", step => "my_step"}
+    WHY:  Same as before but has other parameters were passed.
+
+    URI:  /cgi-bin/my_cgi/my_step
+    STEP: my_step
+    FORM: {step => "my_step"}
+    WHY:  The path method is called which called path_info_map_base
+          which matched $ENV{'PATH_INFO'} using the default regex
+          of qr{^/(\w+)$} and place the result in
+          $self->form->{$self->step_key}.  Path then looks in
+          $self->form->{$self->step_key} for the initial step. See
+          the path_info_map_base method for more information.
+
+    URI:  /cgi-bin/my_cgi/my_step?foo=bar
+    STEP: my_step
+    FORM: {foo => "bar", step => "my_step"}
+    WHY:  Same as before but other parameters were passed.
+
+    URI:  /cgi-bin/my_cgi/my_step?step=other_step
+    STEP: other_step
+    FORM: {step => "other_step"}
+    WHY:  The same procedure took place, but when the PATH_INFO
+          string was matched, the form key "step" already existed
+          and was not replaced by the value from PATH_INFO.
+
+The remaining examples in this section are based on the assumption
+that the following method is installed in your script.
+
+    sub my_step_path_info_map {
+        return [
+            [qr{^/\w+/(\w+)/(\d+)$}, 'foo', 'id'],
+            [qr{^/\w+/(\w+)$}, 'foo'],
+            [qr{^/\w+/(.+)$}, 'anything_else'],
+        ];
+    }
+
+    URI:  /cgi-bin/my_cgi/my_step/bar
+    STEP: my_step
+    FORM: {foo => "bar"}
+    WHY:  The step was matched as in previous examples using
+          path_info_map_base.  However, the form key "foo"
+          was set to "bar" because the second regex returned
+          by the path_info_map hook matched the PATH_INFO string
+          and the corresponding matched value was placed into
+          the form using the keys specified following the regex.
+
+    URI:  /cgi-bin/my_cgi/my_step/bar/1234
+    STEP: my_step
+    FORM: {foo => "bar", id => "1234"}
+    WHY:  Same as the previous example, except that the first
+          regex matched the string.  The first regex had two
+          match groups and two form keys specified.  Note that
+          it is important to order your match regexes in the
+          order that will match the most data.  The third regex
+          would also match this PATH_INFO.
+
+    URI:  /cgi-bin/my_cgi/my_step/some/other/type/of/data
+    STEP: my_step
+    FORM: {anything_else => 'some/other/type/of/data'}
+    WHY:  Same as the previous example, except that the third
+          regex matched.
+
+    URI:  /cgi-bin/my_cgi/my_step/bar?bling=blang
+    STEP: my_step
+    FORM: {foo => "bar", bling => "blang"}
+    WHY:  Same as the first step, but additional QUERY_STRING
+          information was passed.
+
+    URI:  /cgi-bin/my_cgi/my_step/one%20two?bar=three%20four
+    STEP: my_step
+    FORM: {anything_else => "one two", bar => "three four"}
+    WHY:  The third path_info_map regex matched.  Note that the
+          %20 in bar was unescaped by CGI::param, but the %20
+          in anything_else was unescaped by Apache.  If you are
+          not using Apache, this behavior may vary.  CGI::Ex::App
+          doesn't decode parameters mapped from PATH_INFO.
+
+See the path method for more information about finding the initial step
+of the path.
+
+The form method calls CGI::Ex::form which uses CGI::param to retrieve
+GET and POST parameters.  See the form method for more information on
+how GET and POST parameters are parsed.
+
+See the path_info_map_base method, and path_info_map hook for more information
+on how the path_info maps function.
+
+A Dumper($self->dump_history) is very useful for determing what hooks have
+taken place.
+
+=head1 ADDING DATA VALIDATION TO A STEP
+
+CGI::Ex::App uses CGI::Ex::Validate for its data validation.  See CGI::Ex::Validate
+for more information about the many ways you can validate your data.
+
+The default hash_validation hook returns an empty hashref.  This means that passed
+in data is all valid and the script will automatically call the step's finalize method.
+
+The following shows how to some contrived validation to a step called "my_step".
+
+    sub my_step_hash_validation {
+        return {
+            username => {
+                required    => 1,
+                match       => 'm/^(\w+)$/',
+                match_error => 'The $field field may only contain word characters',
+                max_len     => 20,
+            },
+            password => {
+                required => 1,
+                max_len  => 15,
+            },
+            password_verify => {
+                validate_if => 'password',
+                equals      => 'password',
+            },
+            usertype => {
+                required => 1,
+                enum     => [qw(animal vegetable mineral)],
+            },
+        };
+    }
+
+The step will continue to display the html form until all of the fields pass
+validation.
+
+See the hash_validation hook and validate hook for more information about how
+this takes place.
+
+=head1 ADDING JAVASCRIPT DATA VALIDATION TO A STEP
+
+You must first provide a hash_validation hook as explained in the previous section.
+
+Once you have a hash_validation hook, you would place the following tags
+into your HTML template.
+
+    <form name="[% form_name %]" method="post">
+    ...
+    </form>
+    [% js_validation %]
+
+The "form_name" swap-in places a name on the form that the javascript returned by
+the js_validation swap-in will be able to find and check for validity.
+
+See the hash_validation, js_validation, and form_name hooks for more information.
+
+Also, CGI::Ex::validate.js allows for inline errors in addition to or in replacement
+of an alert message.  To use inline errors, you must provide an element in your
+HTML document where this inline message can be placed.  The common way to do it is as
+follows:
+
+    <input type="text" name="username"><br>
+    <span class="error" id="username_error">[% username_error %]</span>
+
+The span around the error allows for the error css class and it provides a location
+that the Javascript validation can populate with errors.  The [% username_error %] provides
+a location for errors generated on the server side to be swapped in.  If there was no error
+the [% username_error %] tag would default to "".
+
+=head1 ADDING ADDITIONAL TEMPLATE VARIABLES
+
+All variables returned by the hash_base, hash_common, hash_form, hash_swap, and
+hash_errors hooks are available for swapping in templates.
+
+The following shows how to add variables using the hash_swap hook on the step "main".
+
+    sub main_hash_swap {
+        return {
+            color   => 'red',
+            choices => [qw(one two three)],
+            "warn"  => sub { warn @_ },
+        };
+    }
+
+You could also return the fields from the hash_common hook and they would be available
+in both the template swapping as well as form filling.
+
+See the hash_base, hash_common, hash_form, hash_swap, hash_errors, swap_template, and
+template_args hooks for more information.
+
+The default template engine used is CGI::Ex::Template which is Template::Toolkit compatible.
+See the CGI::Ex::Template or Template::Toolkit documentation for the types of data
+that can be passed, and for the syntax that can be used.
+
+=head1 ADDING ADDITIONAL FORM FILL VARIABLES
 
 
+All variables returned by the hash_base, hash_common, hash_form, and hash_fill hooks
+are available for filling html fields in on templates.
+
+The following shows how to add variables using the hash_fill hook on the step "main".
+
+    sub main_hash_fill {
+        return {
+            color   => 'red',
+            choices => [qw(one two three)],
+        };
+    }
+
+You could also return the fields from the hash_common hook and they would be available
+in both the form filling as well as in the template swapping.
+
+See the hash_base, hash_common, hash_form, hash_swap, hash_errors, fill_template, and
+fill_args hooks for more information.
+
+The default form filler is CGI::Ex::Fill which is similar to HTML::FillInForm but
+has several benefits.  See the CGI::Ex::Fill module for the available options.
 
 =head1 SYNOPSIS (A LONG "SYNOPSIS")
 
 
 =head1 SYNOPSIS (A LONG "SYNOPSIS")
 
-More examples will come with time.  Here are the basics for now.
 This example script would most likely be in the form of a cgi, accessible via
 the path http://yourhost.com/cgi-bin/my_app (or however you do CGIs on
 your system.  About the best way to get started is to paste the following
 This example script would most likely be in the form of a cgi, accessible via
 the path http://yourhost.com/cgi-bin/my_app (or however you do CGIs on
 your system.  About the best way to get started is to paste the following
@@ -171,8 +573,9 @@ URI '/cgi-bin/my_app' would run the step 'main' first by default.  The
 URI '/cgi-bin/my_app?step=foo' would run the step 'foo' first.  The
 URI '/cgi-bin/my_app/bar' would run the step 'bar' first.
 
 URI '/cgi-bin/my_app?step=foo' would run the step 'foo' first.  The
 URI '/cgi-bin/my_app/bar' would run the step 'bar' first.
 
-CGI::Ex::App allows for running steps in a preset path.  The navigate
-method will go through a step of the path at a time and see if it is
+CGI::Ex::App allows for running steps in a preset path or each step may
+choose the next step that should follow.  The navigate
+method will go through one step of the path at a time and see if it is
 completed (various methods determine the definition of "completed").
 This preset type of path can also be automated using the CGI::Path
 module.  Rather than using a preset path, CGI::Ex::App also has
 completed (various methods determine the definition of "completed").
 This preset type of path can also be automated using the CGI::Path
 module.  Rather than using a preset path, CGI::Ex::App also has
@@ -219,7 +622,7 @@ use external html templates).
 A few things to note about the template:
 
 First, we add a hidden form field called step.  This will be filled in
 A few things to note about the template:
 
 First, we add a hidden form field called step.  This will be filled in
-at a later point with the current step we are on.
+automatically at a later point with the current step we are on.
 
 We provide locations to swap in inline errors.
 
 
 We provide locations to swap in inline errors.
 
@@ -290,150 +693,6 @@ ready to validate so it prints out the template page.
 For more of a real world example, it would be good to read the sample recipe db
 application included at the end of this document.
 
 For more of a real world example, it would be good to read the sample recipe db
 application included at the end of this document.
 
-=head1 DEFAULT PROCESS FLOW
-
-The following pseudo-code describes the process flow
-of the CGI::Ex::App framework.  Several portions of the flow
-are encapsulated in hooks which may be completely overridden to give
-different flow.  All of the default actions are shown.  It may look
-like a lot to follow, but if the process is broken down into the
-discrete operations of step iteration, data validation, and template
-printing the flow feels more natural.
-
-=head2 navigate
-
-The process starts off by calling ->navigate.
-
-    navigate {
-        eval {
-            ->pre_navigate
-            ->nav_loop
-            ->post_navigate
-        }
-        # dying errors will run the ->handle_error method
-
-        ->destroy
-    }
-
-=head2 nav_loop
-
-The nav_loop method will run as follows:
-
-    nav_loop {
-        ->path (get the array of path steps)
-            # look in $ENV{'PATH_INFO'}
-            # look in ->form for ->step_key
-            # make sure step is in ->valid_steps (if defined)
-
-        ->pre_loop($path)
-            # navigation stops if true
-
-        foreach step of path {
-
-            ->morph
-                # check ->allow_morph
-                # check ->allow_nested_morph
-                # ->morph_package (hook - get the package to bless into)
-                # ->fixup_after_morph if morph_package exists
-                # if no package is found, process continues in current file
-
-            ->run_step (hook)
-
-            ->refine_path (hook)
-                # only called if run_step returned false (page not printed)
-                ->next_step (hook) # find next step and add to path
-                ->set_ready_validate(0) (hook)
-
-            ->unmorph
-                # only called if morph worked
-                # ->fixup_before_unmorph if blessed to current package
-
-            # exit loop if ->run_step returned true (page printed)
-
-        } end of foreach step
-
-        ->post_loop
-            # navigation stops if true
-
-        ->default_step
-        ->insert_path (puts the default step into the path)
-        ->nav_loop (called again recursively)
-
-    } end of nav_loop
-
-=head2 run_step (hook)
-
-For each step of the path the following methods will be run
-during the run_step hook.
-
-    run_step {
-        ->pre_step (hook)
-            # exits nav_loop if true
-
-        ->skip (hook)
-            # skips this step if true (stays in nav_loop)
-
-        ->prepare (hook - defaults to true)
-
-        ->info_complete (hook - ran if prepare was true)
-            ->ready_validate (hook)
-            return false if ! ready_validate
-            ->validate (hook - uses CGI::Ex::Validate to validate form info)
-                ->hash_validation (hook)
-                   ->file_val (hook)
-                       ->base_dir_abs
-                       ->base_dir_rel
-                       ->name_module
-                       ->name_step
-                       ->ext_val
-            returns true if validate is true or if nothing to validate
-
-        ->finalize (hook - defaults to true - ran if prepare and info_complete were true)
-
-        if ! ->prepare || ! ->info_complete || ! ->finalize {
-            ->prepared_print
-                ->hash_base (hook)
-                ->hash_common (hook)
-                ->hash_form (hook)
-                ->hash_fill (hook)
-                ->hash_swap (hook)
-                ->hash_errors (hook)
-                # merge form, base, common, and fill into merged fill
-                # merge form, base, common, swap, and errors into merged swap
-                ->print (hook - passed current step, merged swap hash, and merged fill)
-                     ->file_print (hook - uses base_dir_rel, name_module, name_step, ext_print)
-                     ->swap_template (hook - processes the file with CGI::Ex::Template)
-                          ->template_args (hook - passed to CGI::Ex::Template->new)
-                     ->fill_template (hook - fills the any forms with CGI::Ex::Fill)
-                          ->fill_args (hook - passed to CGI::Ex::Fill::fill)
-                     ->print_out (hook - print headers and the content to STDOUT)
-
-            ->post_print (hook - used for anything after the print process)
-
-            # return true to exit from nav_loop
-        }
-
-        ->post_step (hook)
-            # exits nav_loop if true
-
-    } end of run_step
-
-It is important to learn the function and placement of each of the
-hooks in the process flow in order to make the most of CGI::Ex::App.
-It is enough to begin by learning a few common hooks - such as
-hash_validation, hash_swap, and finalize, and then learn about other
-hooks as needs arise.  Sometimes, it is enough to simply override the
-run_step hook and take care of processing the entire step yourself.
-
-Because of the hook based system, and because CGI::Ex::App uses
-sensible defaults, it is very easy to override a little or a lot which
-ends up giving the developer a lot of flexibility.
-
-Consequently, it should be possible to use CGI::Ex::App with the other
-frameworks such as CGI::Application or CGI::Prototype.  For these you
-could simple let each "runmode" call the run_step hook of CGI::Ex::App
-and you will instantly get all of the common process flow for free.
-
 =head1 AVAILABLE METHODS / HOOKS
 
 CGI::Ex::App's dispatch system works on the principles of hooks (which
 =head1 AVAILABLE METHODS / HOOKS
 
 CGI::Ex::App's dispatch system works on the principles of hooks (which
@@ -540,6 +799,28 @@ up the username.  See the get_valid_auth method.
          return lc $user;
      }
 
          return lc $user;
      }
 
+=item clear_app (method)
+
+If the same CGI::Ex::App based object is used to run multiple
+navigate sessions, the clear_app method should be called which
+will attempt to clear as much session information as it can.
+The following items will be cleared:
+
+    cgix
+    vob
+    form
+    cookies
+    stash
+    path
+    path_i
+    history
+    __morph_lineage_start_index
+    __morph_lineage
+    hash_errors
+    hash_fill
+    hash_swap
+    hash_common
+
 =item current_step (method)
 
 Returns the current step that the nav_loop is functioning on.
 =item current_step (method)
 
 Returns the current step that the nav_loop is functioning on.
@@ -1220,6 +1501,70 @@ If navigation runs out of steps to run, the default step found in
 default_step will be run.  This is what allows for us to default
 to the "main" step for many applications.
 
 default_step will be run.  This is what allows for us to default
 to the "main" step for many applications.
 
+=item path_info_map (hook)
+
+Used to map path_info parts to form variables.  Similar to the
+path_info_map_base method.  See the path_info_map_base method
+for a discussion of how to use this hook.
+
+=item path_info_map_base (method)
+
+Called during the default path method.  It is used to custom map portions
+of $ENV{'PATH_INFO'} to form values.  If should return an arrayref of
+arrayrefs where each child arrayref contains a regex qr with match parens
+as the first element of the array.  Subsequent elements of the array are
+the key names to store the corresponding matched value from the regex under.
+The outer arrayref is iterated until it one of child arrayrefs matches
+against $ENV{'PATH_INFO'}.  The matched values are only added to the form if
+there is not already a defined value for that key in the form.
+
+The default value returned by this method looks something like the following:
+
+   sub path_info_map_base {
+       return [[qr{^/(\w+)}, $self->step_key]];
+   }
+
+This example would map the following PATH_INFO string as follows:
+
+   /my_step
+
+   # $self->form->{'step'} now equals "my_step"
+
+The following is another example:
+
+   sub path_info_map_base {
+       return [
+           [qr{^/([^/]+)/(\w+)}, 'username', $self->step_key],
+           [qr{^/(\w+)}, $self->step_key],
+       ];
+   }
+
+   # the PATH_INFO /my_step
+   # still results in
+   # $self->form->{'step'} now equals "my_step"
+
+   # but with the PATH_INFO /my_user/my_step
+   # $self->form->{'step'} now equals "my_step"
+   # and $self->form->{'username'} equals "my_user"
+
+In most cases there is not a need to override the path_info_map_base
+method, but rather override the path_info_map hook for a particular step.
+When the step is being run, just before the run_step hook is called, the
+path_info_map hook is called.  The path_info_map hook is similar to
+the path_info_map_base method, but is used to allow step level manipulation
+of form based on elements in the $ENV{'PATH_INFO'}.
+
+   sub my_step_path_info_map {
+       return [[qr{^/my_step/(\w+)$}, 'username']];
+   }
+
+   # the PATH_INFO /my_step/my_user
+   # results in
+   # $self->form->{'step'} equal to "my_step" because of default path_info_map_base
+   # and $self->form->{'username'} equals "my_user" because of my_step_path_info_map
+
+The section on mapping URIs to steps has additional examples.
+
 =item post_loop (method)
 
 Ran after all of the steps in the loop have been processed (if
 =item post_loop (method)
 
 Ran after all of the steps in the loop have been processed (if
@@ -1566,19 +1911,83 @@ used to abort early before the get_pass_by_user hook is called.
 
 =back
 
 
 =back
 
-=head1 OTHER APPLICATION MODULES
+=head1 HOW DO I SET COOKIES, REDIRECT, ETC
+
+Often in your program you will want to set cookies or bounce to a differnt URL.
+This can be done using either the builtin CGI::Ex object or the built in
+CGI object.  It is suggested that you only use the CGI::Ex methods as it will
+automatically send headers and method calls under cgi, mod_perl1, or mod_perl2.
+The following shows how to do basic items using the CGI::Ex object returned by
+the ->cgix method.
+
+=over 4
+
+=item printing content-type headers
+
+    ### CGI::Ex::App prints headers for you,
+    ### but if you are printing custom types, you can send your own
+    $self->cgix->print_content_type;
+    # SAME AS
+    # $self->cgix->print_content_type('text/html');
+
+=item setting a cookie
+
+    $self->cgix->set_cookie({
+        -name    => "my_key",
+        -value   => 'Some Value',
+        -expires => '1y',
+        -path    => '/',
+    });
+
+=item redirecting to another URL
+
+    $self->cgix->location_bounce("http://somewhereelse.com");
+    $self->exit_nav_loop; # normally should do this to long jump out of navigation
+
+=item making a QUERY_STRING
+
+    my $data  = {foo => "bar", one => "two or three"};
+    my $query = $self->cgix->make_form($data);
+    # $query now equals "foo=bar&one=two%20or%20three"
+
+=item getting form parameters
+
+    my $form = $self->form;
+
+In this example $form would now contain a hashref of all POST and GET parameters
+passed to the server.  The form method calls $self->cgix->get_form
+which in turn uses CGI->param to parse values.  Fields with multiple passed
+values will be in the form of an arrayref.
+
+=item getting cookies
+
+    my $cookies = $self->cookies;
+
+In this example $cookies would be a hashref of all passed in cookies.  The
+cookies method calls $self->cgix->get_cookies which in turn uses CGI->cookie
+to parse values.
+
+=back
+
+See the CGI::Ex and CGI documentation for more information.
+
+=head1 COMPARISON TO OTHER APPLICATION MODULES
 
 The concepts used in CGI::Ex::App are not novel or unique.  However, they
 are all commonly used and very useful.  All application builders were
 built because somebody observed that there are common design patterns
 in CGI building.  CGI::Ex::App differs in that it has found more common design
 
 The concepts used in CGI::Ex::App are not novel or unique.  However, they
 are all commonly used and very useful.  All application builders were
 built because somebody observed that there are common design patterns
 in CGI building.  CGI::Ex::App differs in that it has found more common design
-patterns of CGI's than some and tries to get in the way less than others.
+patterns of CGI's than other application builders and tries to get in the way
+less than others.
 
 CGI::Ex::App is intended to be sub classed, and sub sub classed, and each step
 can choose to be sub classed or not.  CGI::Ex::App tries to remain simple
 while still providing "more than one way to do it."  It also tries to avoid
 making any sub classes have to call ->SUPER:: (although that is fine too).
 
 
 CGI::Ex::App is intended to be sub classed, and sub sub classed, and each step
 can choose to be sub classed or not.  CGI::Ex::App tries to remain simple
 while still providing "more than one way to do it."  It also tries to avoid
 making any sub classes have to call ->SUPER:: (although that is fine too).
 
+And if what you are doing on a particular is far too complicated or custom for
+what CGI::Ex::App provides, CGI::Ex::App makes it trivial to override all behavior.
+
 There are certainly other modules for building CGI applications.  The
 following is a short list of other modules and how CGI::Ex::App is
 different.
 There are certainly other modules for building CGI applications.  The
 following is a short list of other modules and how CGI::Ex::App is
 different.
@@ -1605,6 +2014,9 @@ CGI::Ex::App is different in that it:
       which is only a dispatch.
   * Support for easily jumping around in navigation steps.
   * Support for storing some steps in another package.
       which is only a dispatch.
   * Support for easily jumping around in navigation steps.
   * Support for storing some steps in another package.
+  * Integrated authentication
+  * Integrated form filling
+  * Integrated PATH_INFO mapping
 
 CGI::Ex::App and CGI::Application are similar in that they take care
 of handling headers and they allow for calling other "runmodes" from
 
 CGI::Ex::App and CGI::Application are similar in that they take care
 of handling headers and they allow for calling other "runmodes" from
@@ -1624,13 +2036,13 @@ CGI::Prototype is that it is extremely short (very very short).  The
 system (CGI::Ex::App uses CGI::Ex::Template which is TT compatible).
 CGI::Ex::App is differrent in that it:
 
 system (CGI::Ex::App uses CGI::Ex::Template which is TT compatible).
 CGI::Ex::App is differrent in that it:
 
-  * Offers integrated data validation.
-      CGI::Application has had custom addons created that
-      add some of this functionality.  CGI::Ex::App has the benefit
-      that once validation is created,
   * Offers more hooks into the various phases of each step.
   * Support for easily jumping around in navigation steps.
   * Support for storing only some steps in another package.
   * Offers more hooks into the various phases of each step.
   * Support for easily jumping around in navigation steps.
   * Support for storing only some steps in another package.
+  * Integrated data validation
+  * Integrated authentication
+  * Integrated form filling
+  * Integrated PATH_INFO mapping
 
 =back
 
 
 =back
 
@@ -2188,6 +2600,9 @@ Also see the L<CGI::Ex::Auth> perldoc.
 
 =head1 THANKS
 
 
 =head1 THANKS
 
+The following corporation and individuals contributed in some part to
+the original versions.
+
     Bizhosting.com - giving a problem that fit basic design patterns.
 
     Earl Cahill    - pushing the idea of more generic frameworks.
     Bizhosting.com - giving a problem that fit basic design patterns.
 
     Earl Cahill    - pushing the idea of more generic frameworks.
index 82c3964dda3e155f1feeecffe17ed6987efd9dfd..fe9f776e125cb70732cdbeaf92c8609f33ed5816 100644 (file)
@@ -18,7 +18,7 @@ use MIME::Base64 qw(encode_base64 decode_base64);
 use Digest::MD5 qw(md5_hex);
 use CGI::Ex;
 
 use Digest::MD5 qw(md5_hex);
 use CGI::Ex;
 
-$VERSION = '2.07';
+$VERSION = '2.08';
 
 ###----------------------------------------------------------------###
 
 
 ###----------------------------------------------------------------###
 
index 8b656060ed6556306a3246ca8f8466c48e8d28e5..145f16ac7edcf1784aa1c2b1978b6e8b02973396 100644 (file)
@@ -29,7 +29,7 @@ use vars qw($VERSION
             );
 @EXPORT_OK = qw(conf_read conf_write in_cache);
 
             );
 @EXPORT_OK = qw(conf_read conf_write in_cache);
 
-$VERSION = '2.07';
+$VERSION = '2.08';
 
 $DEFAULT_EXT = 'conf';
 
 
 $DEFAULT_EXT = 'conf';
 
index 14e2271c5a964220158c06e8d7d8df0ce3529dc6..f1c5e9fe0d844f4cc139f3960bb9dcb6263dbca3 100644 (file)
@@ -23,7 +23,7 @@ use CGI::Ex;
 use CGI::Ex::Dump qw(debug ctrace dex_html);
 
 BEGIN {
 use CGI::Ex::Dump qw(debug ctrace dex_html);
 
 BEGIN {
-  $VERSION = '2.07';
+  $VERSION = '2.08';
   $SHOW_TRACE = 0      if ! defined $SHOW_TRACE;
   $IGNORE_EVAL = 0     if ! defined $IGNORE_EVAL;
   $EXTENDED_ERRORS = 1 if ! defined $EXTENDED_ERRORS;
   $SHOW_TRACE = 0      if ! defined $SHOW_TRACE;
   $IGNORE_EVAL = 0     if ! defined $IGNORE_EVAL;
   $EXTENDED_ERRORS = 1 if ! defined $EXTENDED_ERRORS;
index 06b806391c41a0860efc8041e386ad4226b3b620..bffe94d37ee6e839a9b21fabf5d031c90e7c3fc3 100644 (file)
@@ -17,7 +17,7 @@ use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION
 use strict;
 use Exporter;
 
 use strict;
 use Exporter;
 
-$VERSION   = '2.07';
+$VERSION   = '2.08';
 @ISA       = qw(Exporter);
 @EXPORT    = qw(dex dex_warn dex_text dex_html ctrace dex_trace);
 @EXPORT_OK = qw(dex dex_warn dex_text dex_html ctrace dex_trace debug);
 @ISA       = qw(Exporter);
 @EXPORT    = qw(dex dex_warn dex_text dex_html ctrace dex_trace);
 @EXPORT_OK = qw(dex dex_warn dex_text dex_html ctrace dex_trace debug);
index 25e13bee95e9928c0705166056b88c58e9a5e023..6c407806605e35e3fe02031db4ca52b7171d092e 100644 (file)
@@ -24,7 +24,7 @@ use vars qw($VERSION
 use base qw(Exporter);
 
 BEGIN {
 use base qw(Exporter);
 
 BEGIN {
-    $VERSION   = '2.07';
+    $VERSION   = '2.08';
     @EXPORT    = qw(form_fill);
     @EXPORT_OK = qw(fill form_fill html_escape get_tagval_by_key swap_tagval_by_key);
 };
     @EXPORT    = qw(form_fill);
     @EXPORT_OK = qw(fill form_fill html_escape get_tagval_by_key swap_tagval_by_key);
 };
index 03ddce970d94e995e67ece8e7d3eb15fa5a66877..d566b2f860927df14763f96cc64359803b6daa29 100644 (file)
@@ -17,7 +17,7 @@ use strict;
 use base qw(Exporter);
 
 BEGIN {
 use base qw(Exporter);
 
 BEGIN {
-    $VERSION  = '2.07';
+    $VERSION  = '2.08';
 
     @EXPORT = qw(JSONDump);
     @EXPORT_OK = @EXPORT;
 
     @EXPORT = qw(JSONDump);
     @EXPORT_OK = @EXPORT;
@@ -103,7 +103,7 @@ sub js_escape {
     return 'null'  if ! defined $str;
 
     ### allow things that look like numbers to show up as numbers (and those that aren't quite to not)
     return 'null'  if ! defined $str;
 
     ### allow things that look like numbers to show up as numbers (and those that aren't quite to not)
-    return $str if $str =~ /^ -? (?: [0-9]{0,13} \. \d* [1-9] | [1-9][0-9]{0,12}) $/x;
+    return $str if $str =~ /^ -? (?: [1-9][0-9]{0,12} | 0) (?: \. \d* [1-9])? $/x;
 
     my $quote = $self->{'single_quote'} ? "'" : '"';
 
 
     my $quote = $self->{'single_quote'} ? "'" : '"';
 
@@ -117,7 +117,8 @@ sub js_escape {
     utf8::decode($str) if $self->{'utf8'} && &utf8::decode;
 
     ### escape <html> and </html> tags in the text
     utf8::decode($str) if $self->{'utf8'} && &utf8::decode;
 
     ### escape <html> and </html> tags in the text
-    $str =~ s{(</? (?: htm | scrip | !-) | --(?=>) )}{$1$quote+$quote}gx;
+    $str =~ s{(</? (?: htm | scrip | !-) | --(?=>) )}{$1$quote+$quote}gx
+        if ! $self->{'no_tag_splitting'};
 
     ### add nice newlines (unless pretty is off)
     if ($self->{'str_nl'} && length($str) > 80) {
 
     ### add nice newlines (unless pretty is off)
     if ($self->{'str_nl'} && length($str) > 80) {
@@ -359,6 +360,31 @@ greater than 80 characters.  Default is "\n" (if pretty is true).
 If the string is less than 80 characters, or if str_nl is set to "", then the escaped
 string will be contained on a single line.  Setting pretty to 0 effectively sets str_nl equal to "".
 
 If the string is less than 80 characters, or if str_nl is set to "", then the escaped
 string will be contained on a single line.  Setting pretty to 0 effectively sets str_nl equal to "".
 
+=item no_tag_splitting
+
+Default off.  If JSON is embedded in an HTML document and the JSON contains C<< <html> >>,
+C<< </html> >>, C<< <script> >>, C<< </script> >>, C<< <!-- >>, or , C<< --> >> tags, they are
+split apart with a quote, a +, and a quote.  This allows the embedded tags to not affect
+the currently playing JavaScript.
+
+However, if the JSON that is output is intended for deserialization by another non-javascript-engine
+JSON parser, this splitting behavior may cause errors when the JSON is imported.  To avoid the splitting
+behavior in these cases you can use the no_tag_splitting flag to turn off the behavior.
+
+    JSONDump("<html><!-- comment --><script></script></html>");
+
+    Would print
+
+    "<htm"+"l><!-"+"- comment --"+"><scrip"+"t></scrip"+"t></htm"+"l>"
+
+    With the flag
+
+    JSONDump("<html><!-- comment --><script></script></html>", {no_tag_splitting => 1});
+
+    Would print
+
+    "<html><!-- comment --><script></script></html>"
+
 =back
 
 =head1 AUTHORS
 =back
 
 =head1 AUTHORS
index 9adea160d20d0d366215540b157cd38638346115..5f8c1778f89873ce2c2ffd9afeeddee9c6f1d6d2 100644 (file)
@@ -39,7 +39,7 @@ use vars qw($VERSION
             );
 
 BEGIN {
             );
 
 BEGIN {
-    $VERSION = '2.07';
+    $VERSION = '2.08';
 
     $PACKAGE_EXCEPTION   = 'CGI::Ex::Template::Exception';
     $PACKAGE_ITERATOR    = 'CGI::Ex::Template::Iterator';
 
     $PACKAGE_EXCEPTION   = 'CGI::Ex::Template::Exception';
     $PACKAGE_ITERATOR    = 'CGI::Ex::Template::Iterator';
@@ -2852,26 +2852,30 @@ sub define_vmethod {
 }
 
 sub vmethod_as_scalar {
 }
 
 sub vmethod_as_scalar {
-    my ($str, $pat) = @_;
-    $pat = '%s' if ! defined $pat;
+    my $str = shift; $str = ''   if ! defined $str;
+    my $pat = shift; $pat = '%s' if ! defined $pat;
     local $^W;
     local $^W;
-    return sprintf $pat, $str;
+    return @_ ? sprintf($pat, $_[0], $str)
+              : sprintf($pat, $str);
 }
 
 sub vmethod_as_list {
 }
 
 sub vmethod_as_list {
-    my ($ref, $pat, $sep) = @_;
-    $pat = '%s' if ! defined $pat;
-    $sep = ' '  if ! defined $sep;
+    my $ref = shift || return '';
+    my $pat = shift; $pat = '%s' if ! defined $pat;
+    my $sep = shift; $sep = ' '  if ! defined $sep;
     local $^W;
     local $^W;
-    return join($sep, map {sprintf $pat, $_} @$ref);
+    return @_ ? join($sep, map {sprintf $pat, $_[0], $_} @$ref)
+              : join($sep, map {sprintf $pat, $_} @$ref);
 }
 
 sub vmethod_as_hash {
 }
 
 sub vmethod_as_hash {
-    my ($ref, $pat, $sep) = @_;
-    $pat = "%s\t%s" if ! defined $pat;
-    $sep = "\n"  if ! defined $sep;
+    my $ref = shift || return '';
+    my $pat = shift; $pat = "%s\t%s" if ! defined $pat;
+    my $sep = shift; $sep = "\n"     if ! defined $sep;
     local $^W;
     local $^W;
-    return join($sep, map {sprintf $pat, $_, $ref->{$_}} sort keys %$ref);
+    return ! @_    ? join($sep, map {sprintf $pat, $_, $ref->{$_}} sort keys %$ref)
+         : @_ == 1 ? join($sep, map {sprintf $pat, $_[0], $_, $ref->{$_}} sort keys %$ref) # don't get to pick - it applies to the key
+         :           join($sep, map {sprintf $pat, $_[0], $_, $_[1], $ref->{$_}} sort keys %$ref);
 }
 
 sub vmethod_chunk {
 }
 
 sub vmethod_chunk {
@@ -2899,7 +2903,11 @@ sub vmethod_indent {
 sub vmethod_format {
     my $str = shift; $str = ''   if ! defined $str;
     my $pat = shift; $pat = '%s' if ! defined $pat;
 sub vmethod_format {
     my $str = shift; $str = ''   if ! defined $str;
     my $pat = shift; $pat = '%s' if ! defined $pat;
-    return join "\n", map{ sprintf $pat, $_ } split(/\n/, $str);
+    if (@_) {
+        return join "\n", map{ sprintf $pat, $_[0], $_ } split(/\n/, $str);
+    } else {
+        return join "\n", map{ sprintf $pat, $_ } split(/\n/, $str);
+    }
 }
 
 sub vmethod_match {
 }
 
 sub vmethod_match {
index 7cdd753a6f40b6adb73d987a25cde897c950160a..fe89aa9bc9fc45c67ca9d0dee6b91b39b2ea3579 100644 (file)
@@ -789,14 +789,20 @@ This is a filter and is not available via the Text virtual object.
 =item fmt
 
     [% item.fmt('%d') %]
 =item fmt
 
     [% item.fmt('%d') %]
+    [% item.fmt('%6s') %]
+    [% item.fmt('%*s', 6) %]
 
 Similar to format.  Returns a string formatted with the passed pattern.  Default pattern is %s.
 
 =item format
 
 
 Similar to format.  Returns a string formatted with the passed pattern.  Default pattern is %s.
 
 =item format
 
-    [% item.format('%d') %] Print the string out in the specified format.  It is similar to
-    the "fmt" virtual method, except that the item is split on newline and each line is
-    processed separately.
+    [% item.format('%d') %]
+    [% item.format('%6s') %]
+    [% item.format('%*s', 6) %]
+
+Print the string out in the specified format.  It is similar to
+the "fmt" virtual method, except that the item is split on newline and each line is
+processed separately.
 
 =item hash
 
 
 =item hash
 
@@ -928,6 +934,8 @@ Virtual Object.
 =item fmt
 
     [% mylist.fmt('%s', ', ') %]
 =item fmt
 
     [% mylist.fmt('%s', ', ') %]
+    [% mylist.fmt('%6s', ', ') %]
+    [% mylist.fmt('%*s', ', ', 6) %]
 
 Passed a pattern and an string to join on.  Returns a string of the values of the list
 formatted with the passed pattern and joined with the passed string.
 
 Passed a pattern and an string to join on.  Returns a string of the values of the list
 formatted with the passed pattern and joined with the passed string.
@@ -1036,6 +1044,8 @@ Virtual Object.
 =item fmt
 
     [% myhash.fmt('%s => %s', "\n") %]
 =item fmt
 
     [% myhash.fmt('%s => %s', "\n") %]
+    [% myhash.fmt('%4s => %5s', "\n") %]
+    [% myhash.fmt('%*s => %*s', "\n", 4, 5) %]
 
 Passed a pattern and an string to join on.  Returns a string of the key/value pairs
 of the hash formatted with the passed pattern and joined with the passed string.
 
 Passed a pattern and an string to join on.  Returns a string of the key/value pairs
 of the hash formatted with the passed pattern and joined with the passed string.
index 9299a5e916a80042f0040a7bca5054a783ae2982..5bb62e3f362c97540e66bfcaf4c60db2a4d797d6 100644 (file)
@@ -22,7 +22,7 @@ use vars qw($VERSION
             @UNSUPPORTED_BROWSERS
             );
 
             @UNSUPPORTED_BROWSERS
             );
 
-$VERSION = '2.07';
+$VERSION = '2.08';
 
 $DEFAULT_EXT   = 'val';
 $QR_EXTRA      = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/;
 
 $DEFAULT_EXT   = 'val';
 $QR_EXTRA      = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/;
@@ -340,6 +340,7 @@ sub validate_buddy {
         }
       }else{
         foreach my $value (@$values) {
         }
       }else{
         foreach my $value (@$values) {
+          next if ! defined $value;
           $value =~ s{(?$opt:$pat)}{
             my @match = (undef, $1, $2, $3, $4, $5, $6); # limit on the number of matches
             my $copy = $swap;
           $value =~ s{(?$opt:$pat)}{
             my @match = (undef, $1, $2, $3, $4, $5, $6); # limit on the number of matches
             my $copy = $swap;
index 124cad9dc00bfa64bd4f2f6009af8445f8c76c8a..8ac047976a473c7fc9ab25ca4e8a3906a58958a4 100644 (file)
@@ -4,7 +4,7 @@
 *  Based upon CGI/Ex/Validate.pm v1.14 from Perl                     *
 *  For instructions on usage, see perldoc of CGI::Ex::Validate       *
 ***----------------------------------------------------------------**/
 *  Based upon CGI/Ex/Validate.pm v1.14 from Perl                     *
 *  For instructions on usage, see perldoc of CGI::Ex::Validate       *
 ***----------------------------------------------------------------**/
-// $Revision: 1.38 $
+// $Revision: 1.41 $
 
 function Validate () {
  this.error             = vob_error;
 
 function Validate () {
  this.error             = vob_error;
@@ -291,6 +291,7 @@ function vob_validate_buddy (form, field, field_val, N_level, ifs_match) {
  if (n_values == 0 || (n_values == 1 && values[0].length == 0)) {
    for (var i = 0; i < tests.length; i ++) {
      var el = form[field];
  if (n_values == 0 || (n_values == 1 && values[0].length == 0)) {
    for (var i = 0; i < tests.length; i ++) {
      var el = form[field];
+     if (! el) continue;
      var type = el.type;
      if (type && (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit'))
        el.value = values[0] = '' + field_val[tests[i]];
      var type = el.type;
      if (type && (type == 'hidden' || type == 'password' || type == 'text' || type == 'textarea' || type == 'submit'))
        el.value = values[0] = '' + field_val[tests[i]];
@@ -304,7 +305,7 @@ function vob_validate_buddy (form, field, field_val, N_level, ifs_match) {
    if (! this.filter_types('do_not_trim',types).length)
      values[i] = values[i].replace('^\\s+','').replace(new RegExp('\\s+$',''),'');
    if (this.filter_types('trim_control_chars',types).length)
    if (! this.filter_types('do_not_trim',types).length)
      values[i] = values[i].replace('^\\s+','').replace(new RegExp('\\s+$',''),'');
    if (this.filter_types('trim_control_chars',types).length)
-     values[i] = values[i].replace(new RegExp('\t', 'g'),' ').replace(new RegExp('[^\x00-\x1F]+','g'),'');
+     values[i] = values[i].replace(new RegExp('\t', 'g'),' ').replace(new RegExp('[\\x00-\\x1F]+','g'),'');
    if (this.filter_types('to_upper_case',types).length) {
      values[i] = values[i].toUpperCase();
    } else if (this.filter_types('to_lower_case',types).length) {
    if (this.filter_types('to_upper_case',types).length) {
      values[i] = values[i].toUpperCase();
    } else if (this.filter_types('to_lower_case',types).length) {
index 991b07d3723d10ff7bbf9efb6d2eb838816c1436..a2105b615c7e7fe6217396db40b9ca9cad9c7d31 100644 (file)
@@ -8,6 +8,8 @@
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
+<script src="./yaml_load.js"></script>
+<script src="./validate.js"></script>
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
index 7c427080c0bbf938fdd8c993d4aa370511ad005e..bb6e9edcde8b62408514bb9d77ffa2a8289fe626 100644 (file)
@@ -8,6 +8,8 @@
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
+<script src="./yaml_load.js"></script>
+<script src="./validate.js"></script>
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
index 430e5315d32b424a0a91aa35ed549f974d6ec425..0db919ace32fb7ff24d1f03179d710fb8b9215bd 100644 (file)
@@ -8,6 +8,8 @@
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
 
 <script src="../lib/CGI/Ex/yaml_load.js"></script>
 <script src="../lib/CGI/Ex/validate.js"></script>
+<script src="./yaml_load.js"></script>
+<script src="./validate.js"></script>
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
 <script>
 if (! document.yaml_load) {
   document.writeln('<span style="color:red"><h1>Missing document.yaml_load</h1>Path to ../lib/CGI/Ex/yaml_load.js may be invalid.</span>');
index 59bfa52dd0fa8be4c5744679198b47cbe8b41be7..1520d7abcbfcc518f3ae8f6ba9ea7dfc35631582 100644 (file)
@@ -4,9 +4,15 @@
 
 4_app_00_base.t - Check for the basic functionality of CGI::Ex::App.
 
 
 4_app_00_base.t - Check for the basic functionality of CGI::Ex::App.
 
+=head1 NOTE
+
+These tests are extremely stripped down to test the basic path flow.  Normally
+unit tests are useful for garnering information about a module.  For CGI::Ex::App
+it is suggested to stick to live use cases or the CGI::Ex::App perldoc.
+
 =cut
 
 =cut
 
-use Test::More tests => 3;
+use Test::More tests => 9;
 use strict;
 
 {
 use strict;
 
 {
@@ -38,6 +44,8 @@ use strict;
 
   sub step2_hash_validation { return {wow => {required => 1, required_error => 'wow is required'}} }
 
 
   sub step2_hash_validation { return {wow => {required => 1, required_error => 'wow is required'}} }
 
+  sub step2_path_info_map { [[qr{^/step2/(\w+)$}, 'wow']] }
+
   sub step2_file_print { return \ "Some step2 content ([% foo %], [% one %]) <input type=text name=wow>[% wow_error %]" }
 
   sub step2_hash_swap { return {foo => 'bar', one => 'two'} }
   sub step2_file_print { return \ "Some step2 content ([% foo %], [% one %]) <input type=text name=wow>[% wow_error %]" }
 
   sub step2_hash_swap { return {foo => 'bar', one => 'two'} }
@@ -50,6 +58,7 @@ use strict;
 
   sub step3_file_print { return \ "All good" }
 
 
   sub step3_file_print { return \ "All good" }
 
+
 }
 
 ###----------------------------------------------------------------###
 }
 
 ###----------------------------------------------------------------###
@@ -60,7 +69,7 @@ use strict;
 Foo->new({
   form => {},
 })->navigate;
 Foo->new({
   form => {},
 })->navigate;
-ok($Foo::test_stdout eq "Main Content");
+ok($Foo::test_stdout eq "Main Content", "Got the right output");
 
 ###----------------------------------------------------------------###
 
 
 ###----------------------------------------------------------------###
 
@@ -70,7 +79,7 @@ ok($Foo::test_stdout eq "Main Content");
 Foo->new({
   form => {step => 'step2'},
 })->navigate;
 Foo->new({
   form => {step => 'step2'},
 })->navigate;
-ok($Foo::test_stdout eq "Some step2 content (bar, two) <input type=text name=wow value=\"wee\">wow is required");
+ok($Foo::test_stdout eq "Some step2 content (bar, two) <input type=text name=wow value=\"wee\">wow is required", "Got the right output");
 
 ###----------------------------------------------------------------###
 
 
 ###----------------------------------------------------------------###
 
@@ -80,4 +89,42 @@ ok($Foo::test_stdout eq "Some step2 content (bar, two) <input type=text name=wow
 Foo->new({
   form=> {step => 'step2', wow => 'something'},
 })->navigate;
 Foo->new({
   form=> {step => 'step2', wow => 'something'},
 })->navigate;
-ok($Foo::test_stdout eq "All good");
+ok($Foo::test_stdout eq "All good", "Got the right output");
+
+###----------------------------------------------------------------###
+
+#$ENV{'REQUEST_METHOD'} = 'GET';
+#$ENV{'QUERY_STRING'}   = '';
+local $ENV{'PATH_INFO'} = '/step2';
+
+Foo->new({
+  form=> {},
+})->navigate;
+ok($Foo::test_stdout eq "Some step2 content (bar, two) <input type=text name=wow value=\"wee\">wow is required", "Got the right output");
+
+###----------------------------------------------------------------###
+
+#$ENV{'REQUEST_METHOD'} = 'GET';
+#$ENV{'QUERY_STRING'}   = 'wow=something';
+local $ENV{'PATH_INFO'} = '/step2';
+
+my $f = Foo->new({
+  form=> {wow => 'something'},
+})->navigate;
+ok($Foo::test_stdout eq "All good", "Got the right output");
+ok($f->form->{'step'} eq 'step2', "Got the right variable set in form");
+
+###----------------------------------------------------------------###
+
+#$ENV{'REQUEST_METHOD'} = 'GET';
+#$ENV{'QUERY_STRING'}   = '';
+local $ENV{'PATH_INFO'} = '/step2/something';
+
+$f = Foo->new({
+  form => {},
+})->navigate;
+ok($Foo::test_stdout eq "All good", "Got the right output");
+ok($f->form->{'step'} eq 'step2',     "Got the right variable set in form");
+ok($f->form->{'wow'}  eq 'something', "Got the right variable set in form");
+
+###----------------------------------------------------------------###
index 7fa643b5c0ac333018d9cd3569fa824110a56427..c1b17ba0917856449130348e78581fc744109afd 100644 (file)
@@ -14,7 +14,7 @@ BEGIN {
 };
 
 use strict;
 };
 
 use strict;
-use Test::More tests => 515 - ($is_tt ? 103 : 0);
+use Test::More tests => 521 - ($is_tt ? 109 : 0);
 use Data::Dumper qw(Dumper);
 use constant test_taint => 0 && eval { require Taint::Runtime };
 
 use Data::Dumper qw(Dumper);
 use constant test_taint => 0 && eval { require Taint::Runtime };
 
@@ -361,6 +361,14 @@ process_ok('[% {a => "B", c => "D"}.fmt %]' => "a\tB\nc\tD") if ! $is_tt;
 process_ok('[% {a => "B", c => "D"}.fmt("%s:%s") %]' => "a:B\nc:D") if ! $is_tt;
 process_ok('[% {a => "B", c => "D"}.fmt("%s:%s", "; ") %]' => "a:B; c:D") if ! $is_tt;
 
 process_ok('[% {a => "B", c => "D"}.fmt("%s:%s") %]' => "a:B\nc:D") if ! $is_tt;
 process_ok('[% {a => "B", c => "D"}.fmt("%s:%s", "; ") %]' => "a:B; c:D") if ! $is_tt;
 
+process_ok('[% 1.format("%s") %]' => '1') if ! $is_tt;
+process_ok('[% 1.format("%*s", 6) %]' => '     1') if ! $is_tt;
+process_ok('[% 1.format("%-*s", 6) %]' => '1     ') if ! $is_tt;
+
+process_ok('[% 1.fmt("%-*s", 6) %]' => '1     ') if ! $is_tt;
+process_ok('[% [1,2].fmt("%-*s", "|", 6) %]' => '1     |2     ') if ! $is_tt;
+process_ok('[% {1=>2,3=>4}.fmt("%*s:%*s", "|", 3, 3) %]' => '  1:  2|  3:  4') if ! $is_tt;
+
 ###----------------------------------------------------------------###
 ### virtual objects
 
 ###----------------------------------------------------------------###
 ### virtual objects
 
index 29f6482f6ce98864f937aaf30e6ed33c38a06964..f3eea8674332b715395a58c81a71ebf0470fbfc1 100644 (file)
@@ -7,7 +7,7 @@
 =cut
 
 use strict;
 =cut
 
 use strict;
-use Test::More tests => 52;
+use Test::More tests => 57;
 
 use_ok('CGI::Ex::JSONDump');
 
 
 use_ok('CGI::Ex::JSONDump');
 
@@ -80,14 +80,19 @@ test_dump(['a' => 1], "[\"a\",1]", {pretty => 0, array_nl => "\n"});
 
 
 test_dump(1, "1");
 
 
 test_dump(1, "1");
+test_dump(0, "0");
 test_dump('1.0', '"1.0"');
 test_dump('123456789012345', '"123456789012345"');
 test_dump('1.0', '"1.0"');
 test_dump('123456789012345', '"123456789012345"');
+test_dump('0.1', '0.1');
+test_dump('.1', '".1"');
+test_dump('00.1', '"00.1"');
 test_dump('a', '"a"');
 test_dump("\n", '"\\n"');
 test_dump("\\", '"\\\\"');
 test_dump('<script>', '"<scrip"+"t>"');
 test_dump('<script>', "'<scrip'+'t>'", {single_quote => 1});
 test_dump('<html>', '"<htm"+"l>"');
 test_dump('a', '"a"');
 test_dump("\n", '"\\n"');
 test_dump("\\", '"\\\\"');
 test_dump('<script>', '"<scrip"+"t>"');
 test_dump('<script>', "'<scrip'+'t>'", {single_quote => 1});
 test_dump('<html>', '"<htm"+"l>"');
+test_dump('<html>', '"<html>"', {no_tag_splitting => 1});
 test_dump('<!--', '"<!-"+"-"');
 test_dump('-->', '"--"+">"');
 test_dump('---', '"---"');
 test_dump('<!--', '"<!-"+"-"');
 test_dump('-->', '"--"+">"');
 test_dump('---', '"---"');
This page took 0.056904 seconds and 4 git commands to generate.