has
_instance_validations
=> (
is
=>
'rw'
,
init_arg
=>
undef
);
sub
push_to_i18n_lookup {
my
(
$class_or_self
,
@args
) =
@_
;
my
$class
=
ref
(
$class_or_self
) ?
ref
(
$class_or_self
) :
$class_or_self
;
@args
=
$class
unless
@args
;
Valiant::Validations::_add_metadata(
$class
,
'i18n'
,
@args
);
}
sub
i18n_lookup {
my
(
$class_or_self
,
$arg
) =
@_
;
my
$class
=
ref
(
$class_or_self
) ?
ref
(
$class_or_self
) :
$class_or_self
;
no
strict
"refs"
;
my
@proposed
= @{
"${class}::ISA"
};
push
@proposed
,
$class_or_self
->i18n_metadata
if
$class_or_self
->can(
'i18n_metadata'
);
return
grep
{
$_
->can(
'model_name'
) } (
$class
,
@proposed
);
}
sub
validations {
my
(
$class_or_self
,
$arg
) =
@_
;
my
$class
=
ref
(
$class_or_self
) ?
ref
(
$class_or_self
) :
$class_or_self
;
my
@existing
= ();
if
(
defined
(
$arg
)) {
if
(
ref
(
$class_or_self
)) {
my
@existing
= @{
$class_or_self
->_instance_validations||[] };
$class_or_self
->_instance_validations([
$arg
,
@existing
]);
}
else
{
Valiant::Validations::_add_metadata(
$class_or_self
,
'validations'
,
$arg
);
}
}
@existing
= @{
$class_or_self
->_instance_validations||[] }
if
ref
$class_or_self
;
my
@validations
=
$class_or_self
->validations_metadata
if
$class_or_self
->can(
'validations_metadata'
);
return
@validations
,
@existing
;
}
sub
errors_class {
'Valiant::Errors'
}
has
'errors'
=> (
is
=>
'ro'
,
init_arg
=>
undef
,
lazy
=> 1,
default
=>
sub
{
return
use_module(
$_
[0]->errors_class)
->new(
object
=>
$_
[0],
i18n
=>
$_
[0]->i18n);
},
);
sub
has_errors {
return
shift
->errors->size ? 1:0;
}
sub
no_errors {
return
!
shift
->has_errors;
}
has
'_context'
=> (
is
=>
'rw'
,
required
=>0,
predicate
=>
'has_context'
);
sub
get_context {
shift
->_context }
sub
context {
my
(
$self
,
$args
) =
@_
;
$args
=
ref
(
$args
) ?
$args
: [
$args
];
$self
->_context(
$args
);
return
$self
;
}
has
'_csrf_token'
=> (
is
=>
'rw'
,
required
=>0,
init_arg
=>
'csrf_token'
,
predicate
=>
'has_csrf_token'
);
sub
csrf_token {
my
(
$self
,
$arg
) =
@_
;
$self
->_csrf_token(
$arg
)
if
$arg
;
return
$arg
?
$self
:
$self
->_csrf_token;
}
has
'validated'
=> (
is
=>
'rw'
,
required
=>1,
init_args
=>
undef
,
default
=>0);
has
'skip_validation'
=> (
is
=>
'rw'
,
required
=>1,
init_args
=>
undef
,
default
=>0);
sub
skip_validate {
my
(
$self
) =
@_
;
$self
->skip_validation(1);
return
$self
;
}
sub
do_validate {
my
(
$self
) =
@_
;
$self
->skip_validation(0);
return
$self
;
}
sub
default_validator_namepart {
'Validator'
}
sub
default_collection_class {
'Valiant::Validator::Collection'
}
sub
read_attribute_for_validation {
my
(
$self
,
$attribute
) =
@_
;
return
unless
defined
$attribute
;
return
my
$value
=
$self
->
$attribute
if
$self
->can(
$attribute
);
}
sub
_validates_coderef {
my
(
$self
,
$coderef
,
$attributes
,
%options
) =
@_
;
$self
->validations([
$coderef
, \
%options
,
$attributes
]);
return
$self
;
}
sub
_is_reserved_option_key {
my
(
$key
) =
@_
;
return
1
if
$key
eq
'if'
||
$key
eq
'unless'
||
$key
eq
'on'
||
$key
eq
'strict'
||
$key
eq
'allow_blank'
||
$key
eq
'allow_undef'
||
$key
eq
'message'
||
$key
eq
'list'
;
return
0;
}
sub
_prepare_validator_packages {
my
(
$class
,
$key
) =
@_
;
my
$camel
= camelize(
$key
);
my
@packages
=
$class
->_normalize_validator_package(
$camel
);
return
@packages
if
$camel
=~/^\+/;
push
@packages
,
map
{
"${_}::${camel}"
;
}
$class
->default_validator_namespaces;
return
@packages
;
}
sub
default_validator_namespaces {
my
(
$self
) =
@_
;
return
(
'Valiant::ValidatorX'
,
'Valiant::Validator'
);
}
sub
_validator_package {
my
(
$self
,
$key
) =
@_
;
my
@validator_packages
=
$self
->_prepare_validator_packages(
$key
);
my
(
$validator_package
,
@rest
) =
grep
{
my
$package_to_test
=
$_
;
eval
{ use_module
$package_to_test
} ||
do
{
my
$notional_filename
= Module::Runtime::module_notional_filename(
$package_to_test
);
if
($@=~m/^Can't locate
$notional_filename
/) {
debug 1,
"Can't find '$package_to_test' in \@INC"
;
0;
}
else
{
throw_exception
UnexpectedUseModuleError
=> (
package
=>
$package_to_test
,
err
=> $@);
}
}
}
@validator_packages
;
throw_exception(
'NameNotValidator'
,
name
=>
$key
,
packages
=> \
@validator_packages
)
unless
$validator_package
;
debug 1,
"Found $validator_package in \@INC"
;
return
$validator_package
;
}
sub
_create_validator {
my
(
$self
,
$validator_package
,
$args
) =
@_
;
debug 1,
"Trying to create validator from $validator_package"
;
my
$validator
=
$validator_package
->new(
$args
);
return
$validator
;
}
sub
validates {
my
(
$self
,
@validation_proto
) =
@_
;
my
$attributes
=
shift
@validation_proto
;
$attributes
= [
$attributes
]
unless
ref
$attributes
;
my
@options
=
@validation_proto
;
my
(
@validator_info
,
%global_options
) = ();
while
(
@options
) {
my
$args
;
my
$key
=
shift
(
@options
);
if
(blessed(
$key
) &&
$key
->can(
'check'
)) {
$args
= {
constraint
=>
$key
};
$key
=
'check'
;
if
((
ref
(
$options
[0])||
''
) eq
'HASH'
) {
my
$base_args
=
shift
(
@options
);
$args
= +{
%$args
,
%$base_args
};
}
}
elsif
((
ref
(
$key
)||
''
) eq
'CODE'
) {
$args
= {
cb
=>
$key
};
$key
=
'with'
;
if
((
ref
(
$options
[0])||
''
) eq
'HASH'
) {
my
$base_args
=
shift
(
@options
);
$args
= +{
%$args
,
%$base_args
};
}
}
else
{
$args
=
shift
(
@options
);
}
if
(_is_reserved_option_key(
$key
)) {
$global_options
{
$key
} =
$args
;
}
else
{
push
@validator_info
, [
$key
,
$args
];
}
}
my
@validators
= ();
foreach
my
$info
(
@validator_info
) {
my
(
$package_part
,
$args
) =
@$info
;
my
$validator_package
=
$self
->_validator_package(
$package_part
);
unless
((
ref
(
$args
)||
''
) eq
'HASH'
) {
$args
=
$validator_package
->normalize_shortcut(
$args
);
throw_exception
InvalidValidatorArgs
=> (
args
=>
$args
)
unless
ref
(
$args
) eq
'HASH'
;
}
$args
->{strict} = 1
if
$global_options
{strict} and !
exists
$args
->{strict};
$args
->{allow_undef} = 1
if
$global_options
{allow_undef} and !
exists
$args
->{allow_undef};
$args
->{allow_blank} = 1
if
$global_options
{allow_blank} and !
exists
$args
->{allow_blank};
$args
->{message} =
$global_options
{message}
if
exists
$global_options
{message} and !
exists
$args
->{message};
foreach
my
$opt
(
qw(if unless on)
) {
next
unless
my
$val
=
$global_options
{
$opt
};
my
@val
= (
ref
(
$val
)||
''
) eq
'ARRAY'
?
@$val
: (
$val
);
if
(
exists
$args
->{
$opt
}) {
my
$current
=
$args
->{
$opt
};
my
@current
= (
ref
(
$current
)||
''
) eq
'ARRAY'
?
@$current
: (
$current
);
@val
= (
@current
,
@val
);
}
$args
->{
$opt
} = \
@val
;
}
$args
->{attributes} =
$attributes
;
$args
->{model_class} =
$self
;
my
$new_validator
=
$self
->_create_validator(
$validator_package
,
$args
);
push
@validators
,
$new_validator
;
}
my
$coderef
=
sub
{
$_
->validate(
@_
)
foreach
@validators
};
$self
->_validates_coderef(
$coderef
,
$attributes
,
%global_options
);
}
sub
_normalize_validator_package {
my
(
$self
,
$with
) =
@_
;
my
(
$prefix
,
$package
) = (
$with
=~m/^(\+?)(.+)$/);
return
$package
if
$prefix
eq
'+'
;
my
$class
=
ref
(
$self
) ||
$self
;
my
@parts
= ((
split
'::'
,
$class
),
$package
);
my
@project_inc
= ();
while
(
@parts
) {
push
@project_inc
,
join
'::'
, (
@parts
,
$class
->default_validator_namepart,
$package
);
pop
@parts
;
}
push
@project_inc
,
join
'::'
,
$class
->default_validator_namepart,
$package
;
return
@project_inc
;
}
sub
_strip_reserved_options {
my
(
%options
) =
@_
;
my
%reserved
= ();
foreach
my
$key
(
keys
%options
) {
if
(_is_reserved_option_key(
$key
)) {
$reserved
{
$key
} =
delete
$options
{
$key
};
}
}
return
%reserved
;
}
sub
validates_with {
my
(
$self
,
$validators_proto
,
%options
) =
@_
;
my
%reserved
= _strip_reserved_options(
%options
);
my
@with
=
ref
(
$validators_proto
) eq
'ARRAY'
?
@{
$validators_proto
} : (
$validators_proto
);
my
@validators
= ();
VALIDATOR_WITHS:
foreach
my
$with
(
@with
) {
if
( (
ref
(
$with
)||
''
) eq
'CODE'
) {
push
@validators
, [
$with
, \
%options
];
next
VALIDATOR_WITHS;
}
debug 1,
"Trying to find a validator for '$with'"
;
my
@possible_packages
=
$self
->_normalize_validator_package(
$with
);
foreach
my
$package
(
@possible_packages
) {
my
$found_package
=
eval
{
use_module(
$package
);
} ||
do
{
my
$notional_filename
= Module::Runtime::module_notional_filename(
$package
);
if
($@=~m/^Can't locate
$notional_filename
/) {
debug 1,
"Can't find '$package' in \@INC"
;
0;
}
else
{
throw_exception
UnexpectedUseModuleError
=> (
package
=>
$package
,
err
=> $@);
}
};
if
(
$found_package
) {
debug 1,
"Found '$found_package' in \@INC"
;
push
@validators
,
$package
->new(
%options
);
next
VALIDATOR_WITHS;
}
}
throw_exception
General
=> (
msg
=>
"Failed to find validator for '$with' in \@INC"
);
}
my
$collection
= use_module(
$self
->default_collection_class)
->new(
validators
=>\
@validators
,
%reserved
);
$self
->_validates_coderef(
sub
{
$collection
->validate(
@_
) });
}
sub
valid {
my
$self
=
shift
;
$self
->validate(
@_
)
if
@_
|| !
$self
->validated;
return
$self
->errors->size ? 0:1;
}
sub
invalid {
shift
->valid(
@_
) ? 0:1 }
sub
clear_validated {
my
$self
=
shift
;
$self
->errors->clear;
$self
->validated(0);
}
sub
validate {
my
(
$self
,
%args
) =
@_
;
my
$existing_context
=
$args
{context} ?
$args
{context} : [];
my
@existing_context
=
ref
(
$existing_context
) ?
@$existing_context
: (
$existing_context
);
push
@existing_context
, @{
$self
->_context}
if
$self
->has_context;
$args
{context} = \
@existing_context
if
@existing_context
;
return
$self
if
$self
->skip_validation;
return
$self
if
$self
->{_inprocess};
$self
->{_inprocess} =1;
$self
->clear_validated
if
$self
->validated;
$self
->_run_validations(
%args
);
$self
->_run_post_validations(
%args
);
$self
->validated(1);
delete
$self
->{_inprocess};
return
$self
;
}
sub
validate_only {
my
(
$self
,
$attribute
,
%args
) =
@_
;
my
@attributes
=
ref
(
$attribute
) ?
@$attribute
: (
$attribute
);
my
$existing_context
=
$args
{context} ?
$args
{context} : [];
my
@existing_context
=
ref
(
$existing_context
) ?
@$existing_context
: (
$existing_context
);
push
@existing_context
, @{
$self
->_context}
if
$self
->has_context;
$args
{context} = \
@existing_context
if
@existing_context
;
return
$self
if
$self
->skip_validation;
return
$self
if
$self
->{_inprocess};
$self
->{_inprocess} =1;
$self
->clear_validated
if
$self
->validated;
foreach
my
$validation
(
$self
->validations) {
next
unless
$validation
->[2];
my
@attrs_for_validation
= @{
$validation
->[2]};
foreach
my
$attr
(
@attrs_for_validation
) {
next
unless
grep
{
$_
eq
$attr
}
@attributes
;
$validation
->[0](
$self
, +{%{
$validation
->[1]},
%args
});
}
}
$self
->validated(1);
delete
$self
->{_inprocess};
return
$self
;
}
sub
_run_validations {
my
(
$self
,
%args
) =
@_
;
foreach
my
$validation
(
$self
->validations) {
my
%validation_args
= (%{
$validation
->[1]},
%args
);
$validation
->[0](
$self
, \
%validation_args
);
}
}
sub
_run_post_validations {
my
(
$self
,
%args
) =
@_
;
return
;
}
sub
inject_attribute {
my
(
$class
,
$attribute_to_inject
) =
@_
;
eval
"package $class; has $attribute_to_inject => (is=>'ro');"
;
}
1;