CleanCode Perl Libraries
Multi-Lingual Library Maintainability
available: Perl not available: Java available: JavaScript not available: Certified
Class
not available: Testable
Class
not available: Standalone
Mode
available: Diagnostic
Enabled

NAME

CGI::PageValidator::Plugin - Manages validation plugins.

SYNOPSIS

The Plugin class is the superclass for all plugins. It should not be instantiated directly, but only through its subclasses.

REQUIRES

Perl5.005, Data::Diagnostic

DESCRIPTION

This is an abstract class that is the superclass for all plugins used by the PageValidator system. Methods of this class are typically called by CheckItem objects which dynamically load instantiated subclasses of this class.

The actual public interface to this class consists of just these methods: new, examine, and getResult. However, if you are using the basic PageValidator system, you do not need to invoke those; they are invoked from within the CheckItem class.

To create your own plugins, you will have occasion to invoke these methods inside your plugin (which is a subclass): report and setResult. Also in your plugin subclass, you will need to override the examine method in this class.

The following sections describe how to create and use a new plugin in detail.

STEP 1: Add a plugin class reference to a library definition

Here's an example definition for a credit card number entity. Note the plugin property which specifies a unique plugin class name. In general, add "plugin: pluginClassName;" to the definition:

  { name:"CCNumber",
    required:true,
    minLen:14,
    maxLen:17,
    plugin:"CreditCardNum",     <== added this line
    pat:/^\d{14,17}$/,
    patMsg:"just digits (no spaces)"
  },

The validation code is designed to guide you along the way even if you have missing pieces or programming errors. So if we naively stopped after just the one change above, this error message would pop up:

  [JS] *** ERROR: missing CreditCardNum_examine() method
  [PERL] *** ERROR: CGI::PageValidator::CheckItem::_pluginEval>
      Error: eval CreditCardNum:
      Can't locate Validate/Plugin/CreditCardNum.pm in @INC

Note the JavaScript[JS] and Perl[PERL] references above, since the code works almost identically in both versions.

JavaScript note: Actually, the tidy message above will appear in Internet Explorer 5 or above, but NetScape 4 will fail with a JavaScript error, e.g. "CreditCardNum_examine is not defined." because it is not possible to trap the error in NS4.

STEP 2: Add a Stub Class

Create the file [JS] .../CGI/PageValidator/Plugin/pluginClassName.js or [PERL] .../CGI/PageValidator/Plugin/pluginClassName.pm. The [JS] file does not need to contain anything at this step(!) as the validation engine creates the class constructor automatically. The [PERL] file, on the other hand, needs to define the @ISA variable inside the appropriate package to set up proper inheritance. Continuing our credit card example:

  #!/bin/perl -w
  use strict;
  package CGI::PageValidator::Plugin::CreditCardNum;
  @CGI::PageValidator::Plugin::CreditCardNum::ISA =
      qw( CGI::PageValidator::Plugin );
  1;

Now if you execute the engine, [JS] will of course, give the same error message as before, while [PERL] will now show a similar message:

  [JS] *** ERROR: missing CreditCardNum_examine() method
  [PERL] *** ERROR: CGI::PageValidator::Plugin::examine>
    CGI::PageValidator::Plugin::CreditCardNum must override examine() method

STEP 3: Add a Stub Method

Each Plugin subclass will inherit a default examine() method, but this should be viewed as an abstract method which must be overridden; hence, the above error message. Inside this method, you access the value of the current item with the libItemValue field of the object. Similarly, the dependent item, if any, is accessed with the dependentItemValue field.

[JS] Inside the pluginClassName.js file from step 2, add a method pluginClassName_examine. Here's a basic stub for our example:

  function CreditCardNum_examine() {
        var ccnum = this.libItemValue;
        var cctype = this.dependentItemValue;
        this.report("CreditCardNum_examine", ccnum +" / " + cctype, true);
        this.setResult("");
  }

[PERL] Inside the pluginClassName.pm file from step 2, add a method examine. Here's a basic stub for our example:

  sub examine {
        my $this = shift;
        my $ccnum = $this->{libItemValue};
        my $cctype = $this->{dependentItemValue};
        $this->report("$ccnum / $cctype", 1);
        $this->setResult("");
  }
  1;

When we run it (with appropriate diagnostics, and a credit card number and type entered) we get this [JS] result:

        CheckItem::pluginEval> executing CreditCardNum.examine()...
        CreditCardNum_examine> 123412341 / undefined
        CheckItem::pluginEval> finished CreditCardNum.examine()

and this [PERL] result:

        CheckItem::pluginEval> Attempting eval 'CreditCardNum(CCNumber=12341324)'
        Plugin::report> 12341324 / 
        CheckItem::pluginEval> eval 'CreditCardNum' complete: ''

We see that the examine() method overrides correctly, and we see our ccnumber, but not our cctype, since we have not specified any dependency handling yet. Also, note that we are displaying the input credit card number, not a good idea for privacy reasons. Both of these points will be addressed later in this commentary.

STEP 4: Add a Dependency if Needed

If, as in the case of our credit card example, one field has a dependency on another, this dependency is specified in the library entry with the dependsOn property, its value being the name of another library entry.

So referring back to our original sample entry for CCNumber, we should have added two lines instead of just one:

  plugin:"CreditCardNum",       <== references the plugin
  dependsOn:"CCType",           <== references a dependent definition

For credit card number checking, we care about the number, but also the type of credit card, so we add the dependsOn field in the library entry. Then re-running our stub code, we get this [JS] result:

  CheckItem::pluginEval> executing CreditCardNum.examine()...
  CreditCardNum_examine> 123412341 / visa
  CheckItem::pluginEval> finished CreditCardNum.examine()

and this [PERL] result:

  CheckItem::pluginEval> Attempting eval 'CreditCardNum(CCNumber=12341324)'
  Plugin::report> 12341324 / visa
  CheckItem::pluginEval> eval 'CreditCardNum' complete: ''

Reminder about Perl--the Perl file requires several key lines to execute properly: the 'package' line to properly position the class, the 'ISA' line to set up inheritance, and the '1;' terminating line to satisfy Perl module definition requirements. If any of these are omitted, a suitable error message will show up in the output tagged as an error.

STEP 5: Fill in the Stub Method

That's all there is to creating and connecting the plugin. Now you just have to fill in the code to make it check what you need.

First you must access the appropriate data needed to work with (you've seen a glimpse of this in step 3). You have these pre-defined object fields to work with:

libItem

the associated CheckItem object

libItemValue

value of the CheckItem datum

dependentItemName

name of the dependent item(s) from the 'dependsOn' property

dependentItemValue

value(s) of the dependent field

pluginPkg

package for plugin data libraries

Since you also have the parent--a CheckItem object--that is creating/invoking this plugin, you additionally may reference the standard validation properties of that CheckItem object, including required, maxLen, minLen, etc. (See the UserLib module for a complete list.)

Once you have access to the fields and data, you then write code to do your custom validation in the examine method. Your examine method may perform one (or both) of these:

(A) Perform a validity check on entered data. Examples: is the value a multiple of 5? Is the value larger than the value of field x, but only if field y is empty?

(B) Alter parameters for a subsequent standard test based on entered data. Example: only if 'amex' is entered for a credit card type field does the AmexID field becomes required, so the required property is adjusted for that field.

Finally, once your plugin determines whether the value is valid, your code must do something with that information. There are two methods to invoke: report for diagnostic use, and setResult for program use and ultimately for display to the end user.

Call the setResult method to indicate whether the test was successful (with an empty string) or not (with an error message). (If your examine method performs *only* item B, you do not need to call the setResult method.)

Call the report method to indicate what your evaluation has concluded and why, for diagnostic purposes. For further diagnostic output the print, warnPrint, and errPrint methods are available through the Diagnostic object in the diag property.

Note on Sensitive Data

Both setResult and report provide output to some user, be it a developer or an end-user. As such, care must be taken to protect sensitive data. Do not insert libItemValue or dependentItemValue in an argument to either of these methods without wrapping it in a call to the getNameAndValue method of the libItem object. That will partially obscure the value if it is defined in the library as a sensitive entity. So for example, you'll see a credit card number as "*********9352".

Note on Multiple Dependencies

Step 3 showed how to access a single dependent field. You may also have multiple dependent fields, which requires just a bit more work. Consider this library definition (from SysLib) for a year of birth.

  { name => "BirthYear",
    displayName => "Birth year",
    displayGroupName => "Birth date",
    fieldGroupName => "Birthdate",
    required => 1,
    plugin => "Date",
    pluginOption => "earlier",
    dependsOn => ["BirthDay","BirthMonth"],
    minLen => 4,
    maxLen => 4,
    minVal => 1875,
    maxVal => 1995,
    pat => '\d{4}$',
    patMsg => "just 4 digits"
  }

A birthdate, in this implementation, is separated into day, month, and year, in separate fields. We want to validate the complete birthdate involving 3 fields. We (somewhat) arbitrarily select the year field to attach the plugin (called Date), which means that this field BirthYear depends on two other fields, the day and month of birth. So the dependsOn property is an array instead of a simple string.

Inside the examine method, then, the dependentItemName and dependentItemValue must correspondingly be array references. Here's a fragment in Perl:

  sub examine {
        my $this = shift;
        my ($year,$month,$day) = ($this->{libItemValue},undef,undef);
        my @depValueList = @{$this->{dependentItemValue}};
        my @depNameList = @{$this->{dependentItemName}};
        . . .

CONSTRUCTOR

new

PACKAGE->new(libItem, depItem, fieldValue, depValue, pluginPkg)

This constructor is invoked by subclassed plugins via inheritance. It creates a Plugin object which may then validate an input value using custom code rather than with the predefined validation checks available through CheckItem properties (maxLength, minLength, pattern, etc.).

Parameters:

libItem - Base CheckItem object to work on.

depItem - string; name of one or more dependent CheckItem objects. If multiple dependencies, it is simply a comma-separated list of names contained in a string.

fieldValue - string; value corresponding to libItem.

depValue - string or array reference of strings, which must match the defined dependencies for this entity (depItem).

pluginPkg - string; package name for libraries used by plugins.

Returns:

a newly created object

METHODS

getResult

OBJ->getResult()

Returns the stored result of the plugin evaluation.

Returns:

String which is an error message, or the empty string indicating successful validation.

setResult

OBJ->setResult(msg)

Sets a result to be retrieved by a consumer of the object via the getResult method. This method is to be called only from within a subclass's examine method. If the validation by this plugin is successful, this method need not be called, as the default result indicates success.

Parameters:

msg - string; results of the plugin validation.

report

OBJ->report(msg, notable)

Provides diagnostics during plugin evaluation. This method is to be called only from within a subclass's examine method. The msg should contain plain text explaining what happened and why.

Parameters:

msg - string; diagnostic message to report.

notable - optional; boolean; indicates to display the msg or not.

examine

OBJ->examine()

An abstract method to do custom validation; this method *must* be overridden by each subclass. (If not overridden, it just prints a diagnostic error message indicating to do so.) It should typically include calls to $this->report() and $this->setResult().

BUGS

None

AUTHOR

Michael Sorens

VERSION

$Revision: 8 $ $Date: 2006-12-19 21:13:43 -0800 (Tue, 19 Dec 2006) $

SINCE

CleanCode 0.9

SEE ALSO

CGI::PageValidator::LibraryMgr, CGI::PageValidator::CheckItem, CGI::PageValidator::MainLib::SysLib, CGI::PageValidator::MainLib::UserLib

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 417:

=back doesn't take any parameters, but you said =back -- end of CONSTRUCTOR section

Around line 552:

=back doesn't take any parameters, but you said =back -- end of METHODS section


CleanCode Perl Libraries Copyright © 2001-2013 Michael Sorens - Revised 2013.06.30 Get CleanCode at SourceForge.net. Fast, secure and Free Open Source software downloads