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

can 2 keys for id_key
More test

I need more test.

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