CleanCode Perl Libraries |
Home | Perl | Java | PowerShell | C# | SQL | Index | Tools | Download | What's New |
Multi-Lingual Library | Maintainability | ||||||||||||
Perl | Java | JavaScript | Certified Class |
Testable Class |
Standalone Mode |
Diagnostic Enabled |
CGI::PageValidator::Plugin - Manages validation plugins.
The Plugin class is the superclass for all plugins. It should not be instantiated directly, but only through its subclasses.
Perl5.005, Data::Diagnostic
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.
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.
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
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.
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.
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:
the associated CheckItem object
value of the CheckItem
datum
name of the dependent item(s) from the 'dependsOn' property
value(s) of the dependent field
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.
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".
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}};
. . .
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.).
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.
a newly created object
OBJ->getResult()
Returns the stored result of the plugin evaluation.
String which is an error message, or the empty string indicating successful validation.
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.
msg
- string; results of the plugin validation.
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.
msg
- string; diagnostic message to report.
notable
- optional; boolean; indicates to display the msg
or not.
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().
None
Michael Sorens
$Revision: 8 $ $Date: 2006-12-19 21:13:43 -0800 (Tue, 19 Dec 2006) $
CleanCode 0.9
CGI::PageValidator::LibraryMgr, CGI::PageValidator::CheckItem, CGI::PageValidator::MainLib::SysLib, CGI::PageValidator::MainLib::UserLib
Hey! The above document had some coding errors, which are explained below:
=back doesn't take any parameters, but you said =back -- end of CONSTRUCTOR section
=back doesn't take any parameters, but you said =back -- end of METHODS section
Home | Perl | Java | PowerShell | C# | SQL | Index | Tools | Download | What's New |
CleanCode Perl Libraries | Copyright © 2001-2013 Michael Sorens - Revised 2013.06.30 |