NAME
Data::RuledValidator - data validator with rule
DESCRIPTION
Data::RuledValidator is validator of data. This needs rule which is readable by not programmer ... so it is like specification.
WHAT FOR ?
One programmer said;
specification is in code, so documentation is not needed.
Another programmer said;
code is specification, so if I write specification, it is against DRY.
It is execuse of them. They may dislike to write documents, they may be not good at writinng documents, and/or they may think validation check is trivial task. But, if specification is used by programming and we needn't write program, they will start to write specification. And, at last, we need specification.
SYNOPSYS
You can use this without rule file.
BEGIN{
$ENV{REQUEST_METHOD} = "GET";
$ENV{QUERY_STRING} = "page=index&i=9&v=aaaaa&k=bbbb";
}
use Data::RuledValidator;
use CGI;
my $v = Data::RuledValidator->new(obj => CGI->new, method => "param");
print $v->by_sentence("i is num", "k is word", "v is word", "all of i,v,k"); # return 1 if valid
This means that parameater of CGI object, i is number, k is word, v is also word and needs all of i, v and k.
Next example is using following rule;
;;GLOBAL
ID_KEY page
i is number
k is word
v is word
;;index
all of i, k, v
And code is(environmental values are as same as first example):
my $v = Data::RuledValidator->new(obj => CGI->new, method => "param", rule => "validator.rule");
print $v->by_rule; # return 1 if valid
This is as nealy same as first example. left value of ID_KEY, "page" is parameter name to specify rule name to use.
my $q = CGI->new;
$id = $q->param("page");
Now, $id is "index" (see above environmental values in BEGIN block), use rule in "index". Why using CGI object? you have to check new's arguments. "index" rule is following:
;;index
all of i, k, v
Global rule is applied as well.
i is number
k is word
v is word
So it is as same as first example.
RuledValidator GENERAL IDEA
Object
Object has data which you want to check and Object has Method which returns Value(s) from Object's data.
Key
Basicaly, Key is the key which is passed to Object Method.
Value(s)
Value(s) are the returned of the Object Mehotd passed Key.
Operator
Operator is operator to check Value(s).
Condition
Condition is the condition for Operator to judge whether Value(s) is/are valid or not.
USING OPTION
When using Data::RuledValidator, you can use option.
- import_error
-
This defines behavior when plugin is not inported correctly.
use Data::RuledValidator import_error => 0;
If value is 0, do nothing. It is default.
use Data::RuledValidator import_error => 1;
If value is 1, warn.
use Data::RuledValidator import_error => 2;
If value is 2, die.
CONSTRUCTOR
- new
-
my $v = Data::RuledValidator->new(obj => $obj, method => $method, rule => $rule_file_location);
$obj is Object which has values which you want to check. $method is Method of $obj which returns Value(s) which you want to check. $rule_file_location is file location of rule file.
Data::RuledValidator->new(obj => $obj, method => $method);
If you use "by_sentence" and/or you use "by_rule" with argument, no need to use rule.
METHOD for VALIDATION
- by_sentence
-
$v->by_sentence("i is number");
arguments is rule. You can write multiple sentence. It returnes $v object.
- by_rule
-
$v->by_rule(); $v->by_rule($rule);
If $rule is ommitted, using the file which is specified in new. It returnes $v object.
- result
-
$v->result;
The result of validation check. This returned the following structure.
{ 'i_is' => 0, 'v_is' => 0, 'z_match' => 1, }
This means
key 'i' is invalid. key 'v' is invalid. key 'z' is valid.
You can get this result as following:
%result = @$v;
- valid
-
$v->valid;
The result of validation check. The returned value is 1 or 0.
You can get this result as following:
$result = $v;
- reset
-
$v->reset();
The result of validation check is resetted.
RULE SYNTAX
Rule Syntax is very simple.
- ID_KEY Key
-
The right value is key which is pased to Object->Method. The returned value of Object->Method(Key) is used to identify id_key_value.
ID_KEY page
- ;id_key_value
-
start from ; is start of group and the end of this group is the line before next ';'. If the value of Object->Method(ID_KEY) is equal id_key_value, this group validation rule is used.
;index
- ;r;^id_key_value$ (not yet implemented)
-
This is start of group, too. If the value of Object->Method(ID_KEY) is match id_key_value, this group validation rule is used.
;r; ^.*_confirm$
- ;GLOBAL
-
This is start of group, too. but 'GLOBAL' is special name. The rule in this group is inherited by all group.
;GLOBAL i is number w is word
If you write global rule on the top of rule. no need specify ;GLOBAL, they are parsed as GLOBAL.
# The top of file i is number w is word
They will be reagrded as global rule.
- #
-
start from # is comment.
# This is comment
- sentence
-
i is number
sentence has 3 parts, at least.
Key Operator Condition
In example, 'i' is Key, 'is' is Operator and 'number' is Condition.
This means:
return $obj->$method('i') =~/^\d+$/ + 0;
In some case, Operatior can take multiple Condition. It is depends on Operator implementation.
For example, Operator 'match' can multiple Condition.
i match ^[a-z]+$ ^[0-9]+$
Note that:
You cannot use same operator for same key.
i is number i is word
- alias = sentence
-
sentence is as same as above. alias = effects result data structure.
First example is normal version.
Rule:
i is number p is word z match ^\d{3}$
Result Data Structure:
{ 'i_is' => 0, 'p_is' => 0, 'z_match' => 1, }
Next example is version using alias.
id = i is number password = p is word zip = z match ^\d{3}$
Result Data Structure:
{ 'id_is' => 0, 'password_is' => 0, 'zip_match' => 1, }
- Override Global Rule
-
You can override global rule.
;GLOBAL ID_KEY page i is number w is word ;index i is word w is number
If you want delete some rules in GLOBAL in 'index' group.
;index w is n/a w match ^[e-z]+$
If you want delete all GLOBAL rule in 'index' group.
;index GLOABAL is n/a
OPERATORS
- eq
- match
HOW TO ADD OPERATOR
This module has 2 kinds of operator.
- normal operator
-
This is used in sentence.
Key Operator Condition ~~~~~~~~ For example: is, are, match ...
"v is word" returns strucutre like a following:
{ v_is => 1, v_valid => 1, }
- condition operator
-
This is used in sentence.
Key Operator Condition ~~~~~~~~~ This is operator which is used for checking Value(s).
For example: num, alpha, alphanum ...
You can add these opeartor with 2 class method.
- add_operator
-
Data::RuledValidator->add_operator(name => $code);
$code should return code to make closure. For example:
Data::RuledValidaotr->add_operator( 'is' => sub { my($key, $c) = @_; my $sub = Data::RuledValidaotr->_cond_op($c) || ''; unless($sub){ if($c eq 'n/a'){ return $c; }else{ Carp::croak("$c is not defined. you can use; " . join ", ", Data::RuledValidaotr->cond_op); } } return sub {my($self, $v) = @_; $v = shift @$v; return($sub->($self, $v) + 0)}; }, )
$key and $c is Key and Condition. They are given to $code. $code receive them and use them as $code likes. In example, get code ref to use $c(Data::RuledValidaotr->_cond_op($c)).
return sub {my($self, $v) = @_; $v = shift @$v; return($sub->($self, $v) + 0)};
This is the code to return closure. To closure, 5 values are given.
$self, $values, $alias, $obj, $method $self = Data::RuledValidaotr object $values = Value(s). array ref $alias = alias of Key $obj = object given in new $method = method given in new
In example, first 2 values is used.
- add_condition_operator
-
Data::RuledValidator->add_condition_operator(name => $code);
$code should return code ref. For example:
__PACKAGE__->add_condition_operator ( 'mail' => sub{my($self, $v) = @_; return Email::Valid->address($v) ? 1 : 0}, );
PLUGIN
Data::RuledValidator is made with plugins (from version 0.02).
How to create plugins
It's very easy. The name of the modules plugged in this is started from 'Data::RuledValidator::'.
for example:
package Data::RuledValidator::Plugin::Email;
use Email::Valid;
use Email::Valid::Loose;
Data::RuledValidator->add_condition_operator
(
'mail' =>
sub{
my($self, $v) = @_;
return Email::Valid->address($v) ? 1 : ()
},
'mail_loose' =>
sub{
my($self, $v) = @_;
return Email::Valid::Loose->address($v) ? 1 : ()
},
);
1;
That's all. If you want to add normal_operator, use add_operator Class method.
OVERLOADING
$valid = $validator_object; # it is as same as $validator_object->valid;
%valid = @$validator_object; # it is as same as %{$validator_object->result};
NOTE
Now, once rule is parsed, rule is change to code (assemble of closure) and it is stored as class data.
If you use this for CGI, performance is not good. If you use this on mod_perl, it is good idea.
I have some solution;
store code to storable file. store code to shared memory.
TODO
AUTHOR
Ktat, <atusi@pure.ne.jp>
COPYRIGHT
Copyright 2006 by Ktat
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://www.perl.com/perl/misc/Artistic.html