my
%valid_options
=
map
{
$_
=>
undef
} (
'accessor'
,
'auto_deref'
,
'builder'
,
'clearer'
,
'coerce'
,
'default'
,
'documentation'
,
'does'
,
'handles'
,
'init_arg'
,
'insertion_order'
,
'is'
,
'isa'
,
'lazy'
,
'lazy_build'
,
'name'
,
'predicate'
,
'reader'
,
'required'
,
'traits'
,
'trigger'
,
'type_constraint'
,
'weak_ref'
,
'writer'
,
'associated_class'
,
'associated_methods'
,
'__METACLASS__'
,
'provides'
,
'curries'
,
);
our
@CARP_NOT
=
qw(Mouse::Meta::Class)
;
sub
new {
my
$class
=
shift
;
my
$name
=
shift
;
my
$args
=
$class
->Mouse::Object::BUILDARGS(
@_
);
$class
->_process_options(
$name
,
$args
);
$args
->{name} =
$name
;
my
@bad
=
grep
{ !
exists
$valid_options
{
$_
} }
keys
%{
$args
};
if
(
@bad
&&
$class
ne __PACKAGE__){
my
%valid_attrs
= (
map
{
$_
=>
undef
}
grep
{
defined
}
map
{
$_
->init_arg() }
$class
->meta->get_all_attributes()
);
@bad
=
grep
{ !
exists
$valid_attrs
{
$_
} }
@bad
;
}
if
(
@bad
){
Carp::carp(
"Found unknown argument(s) passed to '$name' attribute constructor in '$class': "
. Mouse::Util::english_list(
@bad
));
}
my
$self
=
bless
$args
,
$class
;
if
(
$class
ne __PACKAGE__){
$class
->meta->_initialize_object(
$self
,
$args
);
}
return
$self
;
}
sub
has_read_method {
$_
[0]->has_reader ||
$_
[0]->has_accessor }
sub
has_write_method {
$_
[0]->has_writer ||
$_
[0]->has_accessor }
sub
get_read_method {
$_
[0]->reader ||
$_
[0]->accessor }
sub
get_write_method {
$_
[0]->writer ||
$_
[0]->accessor }
sub
get_read_method_ref{
my
(
$self
) =
@_
;
return
$self
->{_mouse_cache_read_method_ref}
||=
$self
->_get_accessor_method_ref(
'get_read_method'
,
'_generate_reader'
);
}
sub
get_write_method_ref{
my
(
$self
) =
@_
;
return
$self
->{_mouse_cache_write_method_ref}
||=
$self
->_get_accessor_method_ref(
'get_write_method'
,
'_generate_writer'
);
}
sub
interpolate_class{
my
(
$class
,
$args
) =
@_
;
if
(
my
$metaclass
=
delete
$args
->{metaclass}){
$class
= Mouse::Util::resolve_metaclass_alias(
Attribute
=>
$metaclass
);
}
my
@traits
;
if
(
my
$traits_ref
=
delete
$args
->{traits}){
for
(
my
$i
= 0;
$i
< @{
$traits_ref
};
$i
++) {
my
$trait
= Mouse::Util::resolve_metaclass_alias(
Attribute
=>
$traits_ref
->[
$i
],
trait
=> 1);
next
if
$class
->does(
$trait
);
push
@traits
,
$trait
;
push
@traits
,
$traits_ref
->[++
$i
]
if
ref
(
$traits_ref
->[
$i
+1]);
}
if
(
@traits
) {
$class
= Mouse::Meta::Class->create_anon_class(
superclasses
=> [
$class
],
roles
=> \
@traits
,
cache
=> 1,
)->name;
}
}
return
(
$class
,
@traits
);
}
sub
verify_against_type_constraint {
my
(
$self
,
$value
) =
@_
;
my
$type_constraint
=
$self
->{type_constraint};
return
1
if
!
$type_constraint
;
return
1
if
$type_constraint
->check(
$value
);
$self
->_throw_type_constraint_error(
$value
,
$type_constraint
);
}
sub
_throw_type_constraint_error {
my
(
$self
,
$value
,
$type
) =
@_
;
$self
->throw_error(
sprintf
q{Attribute (%s) does not pass the type constraint because: %s}
,
$self
->name,
$type
->get_message(
$value
),
);
}
sub
illegal_options_for_inheritance {
return
qw(reader writer accessor clearer predicate)
;
}
sub
clone_and_inherit_options{
my
$self
=
shift
;
my
$args
=
$self
->Mouse::Object::BUILDARGS(
@_
);
foreach
my
$illegal
(
$self
->illegal_options_for_inheritance) {
if
(
exists
$args
->{
$illegal
} and
exists
$self
->{
$illegal
}) {
$self
->throw_error(
"Illegal inherited option: $illegal"
);
}
}
foreach
my
$name
(
keys
%{
$self
}){
if
(!
exists
$args
->{
$name
}){
$args
->{
$name
} =
$self
->{
$name
};
}
}
my
(
$attribute_class
,
@traits
) =
ref
(
$self
)->interpolate_class(
$args
);
$args
->{traits} = \
@traits
if
@traits
;
foreach
my
$attr
(
keys
%{
$args
}){
if
(
$attr
=~ /\A _mouse_cache_/xms){
delete
$args
->{
$attr
};
}
}
if
(
$args
->{lazy_build}) {
delete
$args
->{
default
};
}
return
$attribute_class
->new(
$self
->name,
$args
);
}
sub
_get_accessor_method_ref {
my
(
$self
,
$type
,
$generator
) =
@_
;
my
$metaclass
=
$self
->associated_class
||
$self
->throw_error(
'No asocciated class for '
.
$self
->name);
my
$accessor
=
$self
->
$type
();
if
(
$accessor
){
return
$metaclass
->get_method_body(
$accessor
);
}
else
{
return
$self
->accessor_metaclass->
$generator
(
$self
,
$metaclass
);
}
}
sub
set_value {
my
(
$self
,
$object
,
$value
) =
@_
;
return
$self
->get_write_method_ref()->(
$object
,
$value
);
}
sub
get_value {
my
(
$self
,
$object
) =
@_
;
return
$self
->get_read_method_ref()->(
$object
);
}
sub
has_value {
my
(
$self
,
$object
) =
@_
;
my
$accessor_ref
=
$self
->{_mouse_cache_predicate_ref}
||=
$self
->_get_accessor_method_ref(
'predicate'
,
'_generate_predicate'
);
return
$accessor_ref
->(
$object
);
}
sub
clear_value {
my
(
$self
,
$object
) =
@_
;
my
$accessor_ref
=
$self
->{_mouse_cache_crealer_ref}
||=
$self
->_get_accessor_method_ref(
'clearer'
,
'_generate_clearer'
);
return
$accessor_ref
->(
$object
);
}
sub
associate_method{
my
(
$attribute
) =
@_
;
$attribute
->{associated_methods}++;
return
;
}
sub
install_accessors{
my
(
$attribute
) =
@_
;
my
$metaclass
=
$attribute
->associated_class;
my
$accessor_class
=
$attribute
->accessor_metaclass;
foreach
my
$type
(
qw(accessor reader writer predicate clearer)
){
if
(
exists
$attribute
->{
$type
}){
my
$generator
=
'_generate_'
.
$type
;
my
$code
=
$accessor_class
->
$generator
(
$attribute
,
$metaclass
);
my
$name
=
$attribute
->{
$type
};
$metaclass
->add_method(
$name
=>
$code
);
$attribute
->associate_method(
$name
);
}
}
if
(
exists
$attribute
->{handles}){
my
%handles
=
$attribute
->_canonicalize_handles();
while
(
my
(
$handle
,
$method_to_call
) =
each
%handles
){
next
if
Mouse::Object->can(
$handle
);
if
(
$metaclass
->has_method(
$handle
)) {
$attribute
->throw_error(
"You cannot overwrite a locally defined method ($handle) with a delegation"
);
}
$metaclass
->add_method(
$handle
=>
$attribute
->_make_delegation_method(
$handle
,
$method_to_call
));
$attribute
->associate_method(
$handle
);
}
}
return
;
}
sub
delegation_metaclass() {
'Mouse::Meta::Method::Delegation'
}
sub
_canonicalize_handles {
my
(
$self
) =
@_
;
my
$handles
=
$self
->{handles};
my
$handle_type
=
ref
$handles
;
if
(
$handle_type
eq
'HASH'
) {
return
%$handles
;
}
elsif
(
$handle_type
eq
'ARRAY'
) {
return
map
{
$_
=>
$_
}
@$handles
;
}
elsif
(
$handle_type
eq
'Regexp'
) {
my
$meta
=
$self
->_find_delegate_metaclass();
return
map
{
$_
=>
$_
}
grep
{ /
$handles
/ }
Mouse::Util::is_a_metarole(
$meta
)
?
$meta
->get_method_list
:
$meta
->get_all_method_names;
}
elsif
(
$handle_type
eq
'CODE'
) {
return
$handles
->(
$self
,
$self
->_find_delegate_metaclass() );
}
else
{
$self
->throw_error(
"Unable to canonicalize the 'handles' option with $handles"
);
}
}
sub
_find_delegate_metaclass {
my
(
$self
) =
@_
;
my
$meta
;
if
(
$self
->{isa}) {
$meta
= Mouse::Meta::Class->initialize(
"$self->{isa}"
);
}
elsif
(
$self
->{does}) {
$meta
= Mouse::Util::get_metaclass_by_name(
"$self->{does}"
);
}
defined
(
$meta
) or
$self
->throw_error(
"Cannot find delegate metaclass for attribute "
.
$self
->name);
return
$meta
;
}
sub
_make_delegation_method {
my
(
$self
,
$handle
,
$method_to_call
) =
@_
;
return
Mouse::Util::load_class(
$self
->delegation_metaclass)
->_generate_delegation(
$self
,
$handle
,
$method_to_call
);
}
1;