my
@CALLBACKS_OPTIONS
= (
qw(if unless on allow_undef allow_blank strict)
);
my
@MESSAGE_OPTIONS
= (
qw(message)
);
has
'object'
=> (
is
=>
'ro'
,
required
=> 1,
weak_ref
=> 1,
);
has
[
'type'
,
'raw_type'
] => (
is
=>
'ro'
,
required
=> 1,
);
has
attribute
=> (
is
=>
'ro'
,
required
=>0,
predicate
=>
'has_attribute'
);
has
bad_value
=> (
is
=>
'ro'
,
required
=>1);
has
options
=> (
is
=>
'ro'
,
required
=>1);
has
i18n
=> (
is
=>
'ro'
,
required
=>1);
sub
i18n_class {
'Valiant::I18N'
}
sub
default_format {
'{{attribute}} {{message}}'
}
around
BUILDARGS
=>
sub
{
my
(
$orig
,
$class
,
@args
) =
@_
;
my
$options
=
$class
->
$orig
(
@args
);
my
(
$object
,
$attribute
,
$type
,
$i18n
,
$set_options
,
$bad_value
) =
delete
@{
$options
}{
qw/object attribute type i18n options bad_value/
};
$i18n
||=
$object
->can(
'i18n'
) ?
$object
->i18n :
Module::Runtime::use_module(
$class
->i18n_class);
unless
(
defined
(
$type
)) {
$type
=
$i18n
->make_tag(
'invalid'
);
}
unless
(
defined
(
$bad_value
)) {
if
(
$attribute
) {
$bad_value
=
$object
->read_attribute_for_validation(
$attribute
);
}
else
{
$bad_value
=
$object
;
}
}
return
+{
object
=>
$object
,
attribute
=>
$attribute
,
type
=>
$type
,
i18n
=>
$i18n
,
raw_type
=>
$type
,
bad_value
=>
$bad_value
,
options
=> +{
%{
$options
||{}},
%{
$set_options
||{}}
},
}
};
sub
full_message {
my
$self
=
shift
;
my
(
$attribute
,
$message
,
$object
,
$i18n
) =
@_
;
$attribute
||=
$self
->attribute
if
$self
->has_attribute;
$message
||=
$self
->message;
$object
||=
$self
->object;
$i18n
||= Scalar::Util::blessed(
$self
) ?
$self
->i18n :
Module::Runtime::use_module(
$self
->i18n_class);
return
$message
unless
defined
(
$attribute
);
my
@defaults
= ();
if
(
$object
->can(
'i18n_scope'
)) {
$attribute
=~s/\.\d+//g;
$attribute
=~s/\[\d+\]//g;
my
$i18n_scope
=
$object
->i18n_scope;
my
@parts
=
split
'\.'
,
$attribute
;
my
$attribute_name
=
pop
@parts
;
my
$namespace
=
join
'/'
,
@parts
if
@parts
;
my
$attributes_scope
=
"${i18n_scope}.errors.models"
;
if
(
$namespace
) {
@defaults
=
map
{
my
$class
=
$_
;
"${attributes_scope}.${\$class->model_name->i18n_key}/${namespace}.attributes.${attribute_name}.format/@{[ $self->type ]}"
,
"${attributes_scope}.${\$class->model_name->i18n_key}/${namespace}.attributes.${attribute_name}.format"
,
"${attributes_scope}.${\$class->model_name->i18n_key}/${namespace}.format"
;
}
grep
{
$_
->model_name->can(
'i18n_key'
) }
$object
->i18n_lookup;
}
else
{
@defaults
=
map
{
my
$class
=
$_
;
"${attributes_scope}.${\$class->model_name->i18n_key}.attributes.${attribute_name}.format/@{[ $self->type ]}"
,
"${attributes_scope}.${\$class->model_name->i18n_key}.attributes.${attribute_name}.format"
,
"${attributes_scope}.${\$class->model_name->i18n_key}.format"
;
}
grep
{
$_
->model_name->can(
'i18n_key'
) }
$object
->i18n_lookup;
}
}
@defaults
=
map
{
$i18n
->make_tag(
$_
) }
@defaults
;
push
@defaults
,
$i18n
->make_tag(
"errors.format.attributes.${attribute}"
);
push
@defaults
,
$i18n
->make_tag(
"errors.format"
);
push
@defaults
,
$self
->default_format;
my
$attr_name
=
do
{
my
$human_attr
=
$attribute
;
$human_attr
=~s/\./ /g;
$human_attr
=~s/\[\d+\]//g;
$human_attr
=~s/_id$//;
$human_attr
=~s/_/ /g;
$human_attr
= autoformat
$human_attr
, {
case
=>
'title'
};
$human_attr
=~s/[\n]//g;
$human_attr
;
};
$attr_name
=
$object
->human_attribute_name(
$attribute
, +{
default
=>
$attr_name
});
return
my
$translated
=
$i18n
->translate(
shift
@defaults
,
default
=> \
@defaults
,
attribute
=>
$attr_name
,
message
=>
$message
);
}
sub
generate_message {
my
(
$self
,
$attribute
,
$type
,
$object
,
$options
,
$i18n
) =
@_
;
$i18n
||= Scalar::Util::blessed(
$self
) ?
$self
->i18n :
Module::Runtime::use_module(
$self
->i18n_class);
$options
||= +{};
$type
=
delete
$options
->{message}
if
$i18n
->is_i18n_tag(
$options
->{message}||
''
);
my
$local_attribute
;
if
(
defined
$attribute
) {
$local_attribute
=
$attribute
if
defined
$attribute
;
$local_attribute
=~s/\[\d+\]//g;
}
my
$value
=
defined
(
$local_attribute
) ?
$object
->read_attribute_for_validation(
$local_attribute
) :
undef
;
my
%options
= (
model
=>
$object
->model_name->human,
attribute
=>
defined
(
$local_attribute
) ?
$object
->human_attribute_name(
$local_attribute
,
$options
) :
undef
,
value
=>
$value
,
object
=>
$object
,
%{
$options
||+{}},
);
my
@defaults
= ();
if
(
$object
->can(
'i18n_scope'
)) {
my
$i18n_scope
=
$object
->i18n_scope;
@defaults
=
map
{
my
$class
=
$_
;
(
defined
(
$local_attribute
) ?
"${i18n_scope}.errors.models.${\$class->model_name->i18n_key}.attributes.${local_attribute}.${$type}"
: ()),
"${i18n_scope}.errors.models.${\$class->model_name->i18n_key}.${$type}"
;
}
grep
{
$_
->model_name->can(
'i18n_key'
)
}
$object
->i18n_lookup
if
$object
->can(
'i18n_lookup'
);
push
@defaults
,
"${i18n_scope}.errors.messages.${$type}"
;
}
push
@defaults
,
"errors.attributes.${local_attribute}.${$type}"
if
defined
(
$local_attribute
);
push
@defaults
,
"errors.messages.${$type}"
;
@defaults
=
map
{
$i18n
->make_tag(
$_
) }
@defaults
;
my
$key
=
shift
(
@defaults
);
if
(
$options
->{message}) {
my
$message
=
delete
$options
->{message};
@defaults
=
ref
(
$message
) ?
@$message
: (
$message
);
}
$options
{
default
} = \
@defaults
;
return
my
$translated
=
$i18n
->translate(
$key
,
%options
);
}
sub
message {
my
$self
=
shift
;
my
$type
=
exists
(
$self
->options->{message}) ?
$self
->options->{message} :
$self
->raw_type;
if
(
$self
->i18n->is_i18n_tag(
$type
)) {
my
%options
= %{
$self
->options};
delete
@options
{
@CALLBACKS_OPTIONS
};
return
$self
->generate_message(
$self
->attribute,
$type
,
$self
->object, \
%options
);
}
elsif
((
ref
(
$type
)||
''
) eq
'CODE'
) {
my
$attribute
=
$self
->attribute;
my
$value
=
defined
(
$attribute
) ?
$self
->object->read_attribute_for_validation(
$attribute
) :
undef
;
my
%options
= (
model
=>
$self
->object->model_name->human,
attribute
=>
defined
(
$attribute
) ?
$self
->object->human_attribute_name(
$attribute
,
$self
->options) :
undef
,
value
=>
$value
,
object
=>
$self
->object,
%{
$self
->options||+{}},
);
delete
@options
{
@CALLBACKS_OPTIONS
,
@MESSAGE_OPTIONS
};
my
$return
=
$type
->(
$self
->object,
$attribute
,
$value
, \
%options
);
return
$self
->i18n->is_i18n_tag(
$return
) ?
$self
->generate_message(
$self
->attribute,
$return
,
$self
->object, \
%options
) :
$return
;
}
elsif
((
ref
(
$type
)||
''
) eq
'SCALAR'
) {
my
$attribute
=
$self
->attribute;
my
$value
=
defined
(
$attribute
) ?
$self
->object->read_attribute_for_validation(
$attribute
) :
undef
;
my
%options
= (
model
=>
$self
->object->model_name->human,
attribute
=>
defined
(
$attribute
) ?
$self
->object->human_attribute_name(
$attribute
,
$self
->options) :
undef
,
value
=>
$value
,
object
=>
$self
->object,
%{
$self
->options||+{}},
);
delete
@options
{
@CALLBACKS_OPTIONS
,
@MESSAGE_OPTIONS
};
my
$translated
=
$$type
;
$translated
=~ s/\{\{([^}]+)\}\}/
defined
(
$options
{$1}) ?
$options
{$1} :
''
/gex;
return
$translated
;
}
else
{
return
$type
;
}
}
sub
detail {
my
$self
=
shift
;
my
%options
= %{
$self
->options};
delete
@options
{
@CALLBACKS_OPTIONS
,
@MESSAGE_OPTIONS
};
return
+{
error
=>
$self
->raw_type,
%options
,
};
}
sub
match {
my
(
$self
,
$attribute
,
$type
,
$options
) =
@_
;
if
(
(
$attribute
||
''
) ne (
$self
->attribute||
''
)
||
(
$type
&& (
$self
->type ne
$type
))
) {
return
0;
}
foreach
my
$key
(%{
$options
||+{}}) {
if
( (
$self
->options->{
$key
}||
''
) ne (
$options
->{
$key
}||
''
)) {
return
0;
}
}
return
1;
}
sub
clone {
my
$self
=
shift
;
my
$class
=
ref
$self
;
return
$class
->new(
object
=>
$self
->object,
attribute
=>
$self
->attribute,
type
=>
$self
->type,
i18n
=>
$self
->i18n,
options
=>
$self
->options,
);
}
sub
strict_match {
my
(
$self
,
$attribute
,
$type
,
$options
) =
@_
;
return
0
unless
$self
->match(
$attribute
,
$type
);
my
%options
= %{
$self
->options};
delete
@options
{
@CALLBACKS_OPTIONS
,
@MESSAGE_OPTIONS
};
return
FreezeThaw::cmpStr(\
%options
,
$options
) == 0 ? 1:0;
}
sub
equals {
my
(
$self
,
$target
) =
@_
;
return
0
unless
ref
(
$self
) eq
ref
(
$target
);
my
$a
= FreezeThaw::freeze
$self
->attributes_for_hash;
my
$b
= FreezeThaw::freeze
$target
->attributes_for_hash;
return
$a
eq
$b
;
}
sub
hash {
my
$self
=
shift
;
return
+{
$self
->attributes_for_hash };
}
sub
attributes_for_hash {
my
$self
=
shift
;
my
%options
= %{
$self
->options};
delete
@options
{
@CALLBACKS_OPTIONS
};
return
(
object
=>
$self
->object,
attribute
=>
$self
->attribute,
raw_type
=>
$self
->raw_type,
map
{
$_
=>
$options
{
$_
} }
sort
keys
%options
,
);
}
1;