our
$VERSION
=
'2.2207'
;
Class::MOP::MiniTrait::apply(__PACKAGE__,
'Moose::Meta::Object::Trait'
);
__PACKAGE__->meta->add_attribute(
'traits'
=> (
reader
=>
'applied_traits'
,
predicate
=>
'has_applied_traits'
,
Class::MOP::_definition_context(),
));
__PACKAGE__->meta->add_attribute(
'role_attribute'
=> (
reader
=>
'role_attribute'
,
predicate
=>
'has_role_attribute'
,
Class::MOP::_definition_context(),
));
sub
does {
my
(
$self
,
$role_name
) =
@_
;
my
$name
=
try
{
Moose::Util::resolve_metatrait_alias(
Attribute
=>
$role_name
)
};
return
0
if
!
defined
(
$name
);
return
$self
->Moose::Object::does(
$name
);
}
sub
_inline_throw_exception {
my
(
$self
,
$exception_type
,
$throw_args
) =
@_
;
return
'die Module::Runtime::use_module("Moose::Exception::'
.
$exception_type
.
'")->new('
. (
$throw_args
||
''
) .
')'
;
}
sub
new {
my
(
$class
,
$name
,
%options
) =
@_
;
$class
->_process_options(
$name
, \
%options
)
unless
$options
{__hack_no_process_options};
delete
$options
{__hack_no_process_options};
my
%attrs
=
(
map
{
$_
=> 1 }
grep
{
defined
}
map
{
$_
->init_arg() }
$class
->meta()->get_all_attributes()
);
my
@bad
=
sort
grep
{ !
$attrs
{
$_
} }
keys
%options
;
if
(
@bad
)
{
my
$s
=
@bad
> 1 ?
's'
:
''
;
my
$list
=
join
"', '"
,
@bad
;
my
$package
=
$options
{definition_context}{
package
};
my
$context
=
$options
{definition_context}{context}
||
'attribute constructor'
;
my
$type
=
$options
{definition_context}{type} ||
'class'
;
my
$location
=
''
;
if
(
defined
(
$package
)) {
$location
=
" in "
;
$location
.=
"$type "
if
$type
;
$location
.=
$package
;
}
Carp::cluck
"Found unknown argument$s '$list' in the $context for '$name'$location"
;
}
return
$class
->SUPER::new(
$name
,
%options
);
}
sub
interpolate_class_and_new {
my
$class
=
shift
;
my
$name
=
shift
;
throw_exception(
MustPassEvenNumberOfAttributeOptions
=>
attribute_name
=>
$name
,
options
=> \
@_
)
if
@_
% 2 == 1;
my
%args
=
@_
;
my
(
$new_class
,
@traits
) =
$class
->interpolate_class(\
%args
);
$new_class
->new(
$name
,
%args
, (
scalar
(
@traits
) ? (
traits
=> \
@traits
) : () ) );
}
sub
interpolate_class {
my
(
$class
,
$options
) =
@_
;
$class
=
ref
(
$class
) ||
$class
;
if
(
my
$metaclass_name
=
delete
$options
->{metaclass} ) {
my
$new_class
= Moose::Util::resolve_metaclass_alias(
Attribute
=>
$metaclass_name
);
if
(
$class
ne
$new_class
) {
if
(
$new_class
->can(
"interpolate_class"
) ) {
return
$new_class
->interpolate_class(
$options
);
}
else
{
$class
=
$new_class
;
}
}
}
my
@traits
;
if
(
my
$traits
=
$options
->{traits}) {
my
$i
= 0;
my
$has_foreign_options
= 0;
while
(
$i
<
@$traits
) {
my
$trait
=
$traits
->[
$i
++];
next
if
ref
(
$trait
);
$trait
= Moose::Util::resolve_metatrait_alias(
Attribute
=>
$trait
)
||
$trait
;
next
if
$class
->does(
$trait
);
push
@traits
,
$trait
;
if
(
$traits
->[
$i
] &&
ref
(
$traits
->[
$i
])) {
$has_foreign_options
= 1
if
any {
$_
ne
'-alias'
&&
$_
ne
'-excludes'
}
keys
%{
$traits
->[
$i
] };
push
@traits
,
$traits
->[
$i
++];
}
}
if
(
@traits
) {
my
%options
= (
superclasses
=> [
$class
],
roles
=> [
@traits
],
);
if
(
$has_foreign_options
) {
$options
{weaken} = 0;
}
else
{
$options
{cache} = 1;
}
my
$anon_class
= Moose::Meta::Class->create_anon_class(
%options
);
$class
=
$anon_class
->name;
}
}
return
(
wantarray
? (
$class
,
@traits
) :
$class
);
}
sub
illegal_options_for_inheritance {
qw(reader writer accessor clearer predicate)
}
sub
clone_and_inherit_options {
my
(
$self
,
%options
) =
@_
;
my
@illegal_options
=
$self
->can(
'illegal_options_for_inheritance'
)
?
$self
->illegal_options_for_inheritance
: ();
my
@found_illegal_options
=
grep
{
exists
$options
{
$_
} &&
exists
$self
->{
$_
} ?
$_
:
undef
}
@illegal_options
;
(
scalar
@found_illegal_options
== 0)
|| throw_exception(
IllegalInheritedOptions
=>
illegal_options
=> \
@found_illegal_options
,
params
=> \
%options
);
$self
->_process_isa_option(
$self
->name, \
%options
);
$self
->_process_does_option(
$self
->name, \
%options
);
if
(
$self
->can(
'interpolate_class'
)) {
(
$options
{metaclass},
my
@traits
) =
$self
->interpolate_class(\
%options
);
my
%seen
;
my
@all_traits
=
grep
{
$seen
{
$_
}++ } @{
$self
->applied_traits || [] },
@traits
;
$options
{traits} = \
@all_traits
if
@all_traits
;
}
$self
->_process_lazy_build_option(
$self
->name, \
%options
)
if
$self
->can(
'_process_lazy_build_option'
);
$self
->clone(
%options
);
}
sub
clone {
my
(
$self
,
%params
) =
@_
;
my
$class
=
delete
$params
{metaclass} ||
ref
$self
;
my
(
@init
,
@non_init
);
foreach
my
$attr
(
grep
{
$_
->has_value(
$self
) } Class::MOP::class_of(
$self
)->get_all_attributes ) {
push
@{
$attr
->has_init_arg ? \
@init
: \
@non_init
},
$attr
;
}
my
%new_params
= ( (
map
{
$_
->
init_arg
=>
$_
->get_value(
$self
) }
@init
),
%params
);
my
$name
=
delete
$new_params
{name};
my
$clone
=
$class
->new(
$name
,
%new_params
,
__hack_no_process_options
=> 1 );
foreach
my
$attr
(
@non_init
) {
$attr
->set_value(
$clone
,
$attr
->get_value(
$self
));
}
return
$clone
;
}
sub
_process_options {
my
(
$class
,
$name
,
$options
) =
@_
;
$class
->_process_is_option(
$name
,
$options
);
$class
->_process_isa_option(
$name
,
$options
);
$class
->_process_does_option(
$name
,
$options
);
$class
->_process_coerce_option(
$name
,
$options
);
$class
->_process_trigger_option(
$name
,
$options
);
$class
->_process_auto_deref_option(
$name
,
$options
);
$class
->_process_lazy_build_option(
$name
,
$options
);
$class
->_process_lazy_option(
$name
,
$options
);
$class
->_process_required_option(
$name
,
$options
);
}
sub
_process_is_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
$options
->{is};
if
(
$options
->{is} eq
'ro'
) {
throw_exception(
"AccessorMustReadWrite"
=>
attribute_name
=>
$name
,
params
=>
$options
,
)
if
exists
$options
->{accessor};
$options
->{reader} ||=
$name
;
}
elsif
(
$options
->{is} eq
'rw'
) {
if
( !
$options
->{accessor} ) {
if
(
$options
->{writer}) {
$options
->{reader} ||=
$name
;
}
else
{
$options
->{accessor} =
$name
;
}
}
}
elsif
(
$options
->{is} eq
'bare'
) {
return
;
}
else
{
throw_exception(
InvalidValueForIs
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
}
}
sub
_process_isa_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
exists
$options
->{isa};
if
(
exists
$options
->{does} ) {
if
(
try
{
$options
->{isa}->can(
'does'
) } ) {
(
$options
->{isa}->does(
$options
->{does} ) )
|| throw_exception(
IsaDoesNotDoTheRole
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
}
else
{
throw_exception(
IsaLacksDoesMethod
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
}
}
if
( blessed(
$options
->{isa} )
&&
$options
->{isa}->can(
'has_coercion'
) ) {
$options
->{type_constraint} =
$options
->{isa};
}
else
{
$options
->{type_constraint}
= Moose::Util::TypeConstraints::find_or_create_isa_type_constraint(
$options
->{isa},
{
package_defined_in
=>
$options
->{definition_context}->{
package
} }
);
}
}
sub
_process_does_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
exists
$options
->{does} && !
exists
$options
->{isa};
if
( blessed(
$options
->{does} )
&&
$options
->{does}->can(
'has_coercion'
) ) {
$options
->{type_constraint} =
$options
->{does};
}
else
{
$options
->{type_constraint}
= Moose::Util::TypeConstraints::find_or_create_does_type_constraint(
$options
->{does},
{
package_defined_in
=>
$options
->{definition_context}->{
package
} }
);
}
}
sub
_process_coerce_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
$options
->{coerce};
(
exists
$options
->{type_constraint} )
|| throw_exception(
CoercionNeedsTypeConstraint
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
throw_exception(
CannotCoerceAWeakRef
=>
attribute_name
=>
$name
,
params
=>
$options
,
)
if
$options
->{weak_ref};
unless
(
$options
->{type_constraint}->has_coercion ) {
my
$type
=
$options
->{type_constraint}->name;
throw_exception(
CannotCoerceAttributeWhichHasNoCoercion
=>
attribute_name
=>
$name
,
type_name
=>
$type
,
params
=>
$options
);
}
}
sub
_process_trigger_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
exists
$options
->{trigger};
(
'CODE'
eq
ref
$options
->{trigger} )
|| throw_exception(
TriggerMustBeACodeRef
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
}
sub
_process_auto_deref_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
$options
->{auto_deref};
(
exists
$options
->{type_constraint} )
|| throw_exception(
CannotAutoDerefWithoutIsa
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
(
$options
->{type_constraint}->is_a_type_of(
'ArrayRef'
)
||
$options
->{type_constraint}->is_a_type_of(
'HashRef'
) )
|| throw_exception(
AutoDeRefNeedsArrayRefOrHashRef
=>
attribute_name
=>
$name
,
params
=>
$options
,
);
}
sub
_process_lazy_build_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
$options
->{lazy_build};
throw_exception(
CannotUseLazyBuildAndDefaultSimultaneously
=>
attribute_name
=>
$name
,
params
=>
$options
,
)
if
exists
$options
->{
default
};
$options
->{lazy} = 1;
$options
->{builder} ||=
"_build_${name}"
;
if
(
$name
=~ /^_/ ) {
$options
->{clearer} ||=
"_clear${name}"
;
$options
->{predicate} ||=
"_has${name}"
;
}
else
{
$options
->{clearer} ||=
"clear_${name}"
;
$options
->{predicate} ||=
"has_${name}"
;
}
}
sub
_process_lazy_option {
my
(
$class
,
$name
,
$options
) =
@_
;
return
unless
$options
->{lazy};
(
exists
$options
->{
default
} ||
defined
$options
->{builder} )
|| throw_exception(
LazyAttributeNeedsADefault
=>
params
=>
$options
,
attribute_name
=>
$name
,
);
}
sub
_process_required_option {
my
(
$class
,
$name
,
$options
) =
@_
;
if
(
$options
->{required}
&& !(
( !
exists
$options
->{init_arg} ||
defined
$options
->{init_arg} )
||
exists
$options
->{
default
}
||
defined
$options
->{builder}
)
) {
throw_exception(
RequiredAttributeNeedsADefault
=>
params
=>
$options
,
attribute_name
=>
$name
,
);
}
}
sub
initialize_instance_slot {
my
(
$self
,
$meta_instance
,
$instance
,
$params
) =
@_
;
my
$init_arg
=
$self
->init_arg();
my
$val
;
my
$value_is_set
;
if
(
defined
(
$init_arg
) and
exists
$params
->{
$init_arg
}) {
$val
=
$params
->{
$init_arg
};
$value_is_set
= 1;
}
else
{
return
if
$self
->is_lazy;
my
$class_name
= blessed(
$instance
);
throw_exception(
'AttributeIsRequired'
,
attribute_name
=>
$self
->name,
(
defined
$init_arg
? (
attribute_init_arg
=>
$init_arg
) : () ),
class_name
=>
$class_name
,
params
=>
$params
,
)
if
$self
->is_required
&& !
$self
->has_default
&& !
$self
->has_builder;
if
(
$self
->has_default) {
$val
=
$self
->
default
(
$instance
);
$value_is_set
= 1;
}
elsif
(
$self
->has_builder) {
$val
=
$self
->_call_builder(
$instance
);
$value_is_set
= 1;
}
}
return
unless
$value_is_set
;
$val
=
$self
->_coerce_and_verify(
$val
,
$instance
);
$self
->set_initial_value(
$instance
,
$val
);
if
(
ref
$val
&&
$self
->is_weak_ref ) {
$self
->_weaken_value(
$instance
);
}
}
sub
_call_builder {
my
(
$self
,
$instance
) =
@_
;
my
$builder
=
$self
->builder();
return
$instance
->
$builder
()
if
$instance
->can(
$self
->builder );
throw_exception(
BuilderDoesNotExist
=>
instance
=>
$instance
,
attribute
=>
$self
,
);
}
sub
_make_initializer_writer_callback {
my
$self
=
shift
;
my
(
$meta_instance
,
$instance
,
$slot_name
) =
@_
;
my
$old_callback
=
$self
->SUPER::_make_initializer_writer_callback(
@_
);
return
sub
{
$old_callback
->(
$self
->_coerce_and_verify(
$_
[0],
$instance
));
};
}
sub
set_value {
my
(
$self
,
$instance
,
@args
) =
@_
;
my
$value
=
$args
[0];
my
$class_name
= blessed(
$instance
);
if
(
$self
->is_required and not
@args
) {
throw_exception(
'AttributeIsRequired'
,
attribute_name
=>
$self
->name,
(
defined
$self
->init_arg
? (
attribute_init_arg
=>
$self
->init_arg )
: ()
),
class_name
=>
$class_name
,
);
}
$value
=
$self
->_coerce_and_verify(
$value
,
$instance
);
my
@old
;
if
(
$self
->has_trigger &&
$self
->has_value(
$instance
) ) {
@old
=
$self
->get_value(
$instance
,
'for trigger'
);
}
$self
->SUPER::set_value(
$instance
,
$value
);
if
(
ref
$value
&&
$self
->is_weak_ref ) {
$self
->_weaken_value(
$instance
);
}
if
(
$self
->has_trigger) {
$self
->trigger->(
$instance
,
$value
,
@old
);
}
}
sub
_inline_set_value {
my
$self
=
shift
;
my
(
$instance
,
$value
,
$tc
,
$coercion
,
$message
,
$for_constructor
) =
@_
;
my
$old
=
'@old'
;
my
$copy
=
'$val'
;
$tc
||=
'$type_constraint'
;
$coercion
||=
'$type_coercion'
;
$message
||=
'$type_message'
;
my
@code
;
if
(
$self
->_writer_value_needs_copy) {
push
@code
,
$self
->_inline_copy_value(
$value
,
$copy
);
$value
=
$copy
;
}
push
@code
,
$self
->_inline_check_required
unless
$for_constructor
;
push
@code
,
$self
->_inline_tc_code(
$value
,
$tc
,
$coercion
,
$message
);
push
@code
,
$self
->_inline_get_old_value_for_trigger(
$instance
,
$old
)
unless
$for_constructor
;
push
@code
, (
$self
->SUPER::_inline_set_value(
$instance
,
$value
),
$self
->_inline_weaken_value(
$instance
,
$value
),
);
push
@code
,
$self
->_inline_trigger(
$instance
,
$value
,
$old
)
unless
$for_constructor
;
return
@code
;
}
sub
_writer_value_needs_copy {
my
$self
=
shift
;
return
$self
->should_coerce;
}
sub
_inline_copy_value {
my
$self
=
shift
;
my
(
$value
,
$copy
) =
@_
;
return
'my '
.
$copy
.
' = '
.
$value
.
';'
}
sub
_inline_check_required {
my
$self
=
shift
;
return
unless
$self
->is_required;
my
$throw_params
=
sprintf
(
<<'EOF', quotemeta( $self->name ) );
attribute_name => "%s",
class_name => $class_name,
EOF
$throw_params
.=
sprintf
(
'attribute_init_arg => "%s",'
,
quotemeta
(
$self
->init_arg )
)
if
defined
$self
->init_arg;
my
$throw
=
$self
->_inline_throw_exception(
'AttributeIsRequired'
,
$throw_params
);
return
sprintf
(
<<'EOF', $throw );
if ( @_ < 2 ) {
%s;
}
EOF
}
sub
_inline_tc_code {
my
$self
=
shift
;
my
(
$value
,
$tc
,
$coercion
,
$message
,
$is_lazy
) =
@_
;
return
(
$self
->_inline_check_coercion(
$value
,
$tc
,
$coercion
,
$is_lazy
,
),
$self
->_inline_check_constraint(
$value
,
$tc
,
$message
,
$is_lazy
,
),
);
}
sub
_inline_check_coercion {
my
$self
=
shift
;
my
(
$value
,
$tc
,
$coercion
) =
@_
;
return
unless
$self
->should_coerce &&
$self
->type_constraint->has_coercion;
if
(
$self
->type_constraint->can_be_inlined ) {
return
(
'if (! ('
.
$self
->type_constraint->_inline_check(
$value
) .
')) {'
,
$value
.
' = '
.
$coercion
.
'->('
.
$value
.
');'
,
'}'
,
);
}
else
{
return
(
'if (!'
.
$tc
.
'->('
.
$value
.
')) {'
,
$value
.
' = '
.
$coercion
.
'->('
.
$value
.
');'
,
'}'
,
);
}
}
sub
_inline_check_constraint {
my
$self
=
shift
;
my
(
$value
,
$tc
,
$message
) =
@_
;
return
unless
$self
->has_type_constraint;
my
$attr_name
=
quotemeta
(
$self
->name);
if
(
$self
->type_constraint->can_be_inlined ) {
return
(
'if (! ('
.
$self
->type_constraint->_inline_check(
$value
) .
')) {'
,
'my $msg = do { local $_ = '
.
$value
.
'; '
.
$message
.
'->('
.
$value
.
');'
.
'};'
.
$self
->_inline_throw_exception(
ValidationFailedForInlineTypeConstraint
=>
'type_constraint_message => $msg , '
.
'class_name => $class_name, '
.
'attribute_name => "'
.
$attr_name
.
'",'
.
'value => '
.
$value
).
';'
,
'}'
,
);
}
else
{
return
(
'if (!'
.
$tc
.
'->('
.
$value
.
')) {'
,
'my $msg = do { local $_ = '
.
$value
.
'; '
.
$message
.
'->('
.
$value
.
');'
.
'};'
.
$self
->_inline_throw_exception(
ValidationFailedForInlineTypeConstraint
=>
'type_constraint_message => $msg , '
.
'class_name => $class_name, '
.
'attribute_name => "'
.
$attr_name
.
'",'
.
'value => '
.
$value
).
';'
,
'}'
,
);
}
}
sub
_inline_get_old_value_for_trigger {
my
$self
=
shift
;
my
(
$instance
,
$old
) =
@_
;
return
unless
$self
->has_trigger;
return
(
'my '
.
$old
.
' = '
.
$self
->_inline_instance_has(
$instance
),
'? '
.
$self
->_inline_instance_get(
$instance
),
': ();'
,
);
}
sub
_inline_weaken_value {
my
$self
=
shift
;
my
(
$instance
,
$value
) =
@_
;
return
unless
$self
->is_weak_ref;
my
$mi
=
$self
->associated_class->get_meta_instance;
return
(
$mi
->inline_weaken_slot_value(
$instance
,
$self
->name),
'if ref '
.
$value
.
';'
,
);
}
sub
_inline_trigger {
my
$self
=
shift
;
my
(
$instance
,
$value
,
$old
) =
@_
;
return
unless
$self
->has_trigger;
return
'$trigger->('
.
$instance
.
', '
.
$value
.
', '
.
$old
.
');'
;
}
sub
_eval_environment {
my
$self
=
shift
;
my
$env
= { };
$env
->{
'$trigger'
} = \(
$self
->trigger)
if
$self
->has_trigger;
$env
->{
'$attr_default'
} = \(
$self
->
default
)
if
$self
->has_default;
if
(
$self
->has_type_constraint) {
my
$tc_obj
=
$self
->type_constraint;
$env
->{
'$type_constraint'
} = \(
$tc_obj
->_compiled_type_constraint
)
unless
$tc_obj
->can_be_inlined;
$env
->{
'$type_coercion'
} = \(
$tc_obj
->coercion->_compiled_type_coercion
)
if
$tc_obj
->has_coercion;
$env
->{
'$type_message'
} = \(
$tc_obj
->has_message ?
$tc_obj
->message :
$tc_obj
->_default_message
);
$env
= {
%$env
, %{
$tc_obj
->inline_environment } };
}
$env
->{
'$class_name'
} = \(
$self
->associated_class->name);
$env
->{
'$attr'
} = \
$self
if
$self
->has_initializer &&
$self
->is_lazy;
$env
->{
'$meta'
} = \(
$self
->associated_class);
return
$env
;
}
sub
_weaken_value {
my
(
$self
,
$instance
) =
@_
;
my
$meta_instance
= Class::MOP::Class->initialize( blessed(
$instance
) )
->get_meta_instance;
$meta_instance
->weaken_slot_value(
$instance
,
$self
->name );
}
sub
get_value {
my
(
$self
,
$instance
,
$for_trigger
) =
@_
;
if
(
$self
->is_lazy) {
unless
(
$self
->has_value(
$instance
)) {
my
$value
;
if
(
$self
->has_default) {
$value
=
$self
->
default
(
$instance
);
}
elsif
(
$self
->has_builder ) {
$value
=
$self
->_call_builder(
$instance
);
}
$value
=
$self
->_coerce_and_verify(
$value
,
$instance
);
$self
->set_initial_value(
$instance
,
$value
);
if
(
ref
$value
&&
$self
->is_weak_ref ) {
$self
->_weaken_value(
$instance
);
}
}
}
if
(
$self
->should_auto_deref && !
$for_trigger
) {
my
$type_constraint
=
$self
->type_constraint;
if
(
$type_constraint
->is_a_type_of(
'ArrayRef'
)) {
my
$rv
=
$self
->SUPER::get_value(
$instance
);
return
unless
defined
$rv
;
return
wantarray
? @{
$rv
} :
$rv
;
}
elsif
(
$type_constraint
->is_a_type_of(
'HashRef'
)) {
my
$rv
=
$self
->SUPER::get_value(
$instance
);
return
unless
defined
$rv
;
return
wantarray
? %{
$rv
} :
$rv
;
}
else
{
throw_exception(
CannotAutoDereferenceTypeConstraint
=>
type_name
=>
$type_constraint
->name,
instance
=>
$instance
,
attribute
=>
$self
);
}
}
else
{
return
$self
->SUPER::get_value(
$instance
);
}
}
sub
_inline_get_value {
my
$self
=
shift
;
my
(
$instance
,
$tc
,
$coercion
,
$message
) =
@_
;
my
$slot_access
=
$self
->_inline_instance_get(
$instance
);
$tc
||=
'$type_constraint'
;
$coercion
||=
'$type_coercion'
;
$message
||=
'$type_message'
;
return
(
$self
->_inline_check_lazy(
$instance
,
$tc
,
$coercion
,
$message
),
$self
->_inline_return_auto_deref(
$slot_access
),
);
}
sub
_inline_check_lazy {
my
$self
=
shift
;
my
(
$instance
,
$tc
,
$coercion
,
$message
) =
@_
;
return
unless
$self
->is_lazy;
my
$slot_exists
=
$self
->_inline_instance_has(
$instance
);
return
(
'if (!'
.
$slot_exists
.
') {'
,
$self
->_inline_init_from_default(
$instance
,
'$default'
,
$tc
,
$coercion
,
$message
,
'lazy'
),
'}'
,
);
}
sub
_inline_init_from_default {
my
$self
=
shift
;
my
(
$instance
,
$default
,
$tc
,
$coercion
,
$message
,
$for_lazy
) =
@_
;
if
(!(
$self
->has_default ||
$self
->has_builder)) {
throw_exception(
LazyAttributeNeedsADefault
=>
attribute
=>
$self
);
}
return
(
$self
->_inline_generate_default(
$instance
,
$default
),
$self
->has_type_constraint
? (
$self
->_inline_check_coercion(
$default
,
$tc
,
$coercion
,
$for_lazy
),
$self
->_inline_check_constraint(
$default
,
$tc
,
$message
,
$for_lazy
))
: (),
$self
->_inline_init_slot(
$instance
,
$default
),
$self
->_inline_weaken_value(
$instance
,
$default
),
);
}
sub
_inline_generate_default {
my
$self
=
shift
;
my
(
$instance
,
$default
) =
@_
;
if
(
$self
->has_default) {
my
$source
=
'my '
.
$default
.
' = $attr_default'
;
$source
.=
'->('
.
$instance
.
')'
if
$self
->is_default_a_coderef;
return
$source
.
';'
;
}
elsif
(
$self
->has_builder) {
my
$builder
= B::perlstring(
$self
->builder);
my
$builder_str
=
quotemeta
(
$self
->builder);
my
$attr_name_str
=
quotemeta
(
$self
->name);
return
(
'my '
.
$default
.
';'
,
'if (my $builder = '
.
$instance
.
'->can('
.
$builder
.
')) {'
,
$default
.
' = '
.
$instance
.
'->$builder;'
,
'}'
,
'else {'
,
'my $class = ref('
.
$instance
.
') || '
.
$instance
.
';'
,
$self
->_inline_throw_exception(
BuilderMethodNotSupportedForInlineAttribute
=>
'class_name => $class,'
.
'attribute_name => "'
.
$attr_name_str
.
'",'
.
'instance => '
.
$instance
.
','
.
'builder => "'
.
$builder_str
.
'"'
) .
';'
,
'}'
,
);
}
else
{
confess(
"Can't generate a default for "
.
$self
->name
.
" since no default or builder was specified"
);
}
}
sub
_inline_init_slot {
my
$self
=
shift
;
my
(
$inv
,
$value
) =
@_
;
if
(
$self
->has_initializer) {
return
'$attr->set_initial_value('
.
$inv
.
', '
.
$value
.
');'
;
}
else
{
return
$self
->_inline_instance_set(
$inv
,
$value
) .
';'
;
}
}
sub
_inline_return_auto_deref {
my
$self
=
shift
;
return
'return '
.
$self
->_auto_deref(
@_
) .
';'
;
}
sub
_auto_deref {
my
$self
=
shift
;
my
(
$ref_value
) =
@_
;
return
$ref_value
unless
$self
->should_auto_deref;
my
$type_constraint
=
$self
->type_constraint;
my
$sigil
;
if
(
$type_constraint
->is_a_type_of(
'ArrayRef'
)) {
$sigil
=
'@'
;
}
elsif
(
$type_constraint
->is_a_type_of(
'HashRef'
)) {
$sigil
=
'%'
;
}
else
{
confess(
'Can not auto de-reference the type constraint \''
.
$type_constraint
->name
.
'\''
);
}
return
'wantarray '
.
'? '
.
$sigil
.
'{ ('
.
$ref_value
.
') || return } '
.
': ('
.
$ref_value
.
')'
;
}
sub
accessor_metaclass {
'Moose::Meta::Method::Accessor'
}
sub
install_accessors {
my
$self
=
shift
;
$self
->SUPER::install_accessors(
@_
);
$self
->install_delegation
if
$self
->has_handles;
return
;
}
sub
_check_associated_methods {
my
$self
=
shift
;
unless
(
@{
$self
->associated_methods }
|| (
$self
->_is_metadata ||
''
) eq
'bare'
) {
Carp::cluck(
'Attribute ('
.
$self
->name .
') of class '
.
$self
->associated_class->name
.
' has no associated methods'
.
' (did you mean to provide an "is" argument?)'
.
"\n"
)
}
}
sub
_process_accessors {
my
$self
=
shift
;
my
(
$type
,
$accessor
,
$generate_as_inline_methods
) =
@_
;
$accessor
= (
keys
%$accessor
)[0]
if
(
ref
(
$accessor
) ||
''
) eq
'HASH'
;
my
$method
=
$self
->associated_class->get_method(
$accessor
);
if
(
$method
&&
$method
->isa(
'Class::MOP::Method::Accessor'
) ) {
unless
(
$method
->associated_attribute->name eq
$self
->name
&& (
$generate_as_inline_methods
&& !
$method
->is_inline ) ) {
my
$other_attr
=
$method
->associated_attribute;
my
$msg
=
sprintf
(
'You are overwriting a %s (%s) for the %s attribute'
,
$method
->accessor_type,
$accessor
,
$other_attr
->name,
);
if
(
my
$method_context
=
$method
->definition_context ) {
$msg
.=
sprintf
(
' (defined at %s line %s)'
,
$method_context
->{file},
$method_context
->{line},
)
if
defined
$method_context
->{file}
&&
$method_context
->{line};
}
$msg
.=
sprintf
(
' with a new %s method for the %s attribute'
,
$type
,
$self
->name,
);
if
(
my
$self_context
=
$self
->definition_context ) {
$msg
.=
sprintf
(
' (defined at %s line %s)'
,
$self_context
->{file},
$self_context
->{line},
)
if
defined
$self_context
->{file}
&&
$self_context
->{line};
}
Carp::cluck(
$msg
);
}
}
if
(
$method
&& !
$method
->is_stub
&& !
$method
->isa(
'Class::MOP::Method::Accessor'
)
&& ( !
$self
->definition_context
||
$method
->package_name eq
$self
->definition_context->{
package
} )
) {
Carp::cluck(
"You are overwriting a locally defined method ($accessor) with "
.
"an accessor"
);
}
if
( !
$self
->associated_class->has_method(
$accessor
)
&&
$self
->associated_class->has_package_symbol(
'&'
.
$accessor
) ) {
Carp::cluck(
"You are overwriting a locally defined function ($accessor) with "
.
"an accessor"
);
}
$self
->SUPER::_process_accessors(
@_
);
}
sub
remove_accessors {
my
$self
=
shift
;
$self
->SUPER::remove_accessors(
@_
);
$self
->remove_delegation
if
$self
->has_handles;
return
;
}
sub
install_delegation {
my
$self
=
shift
;
my
%handles
=
$self
->_canonicalize_handles;
my
$associated_class
=
$self
->associated_class;
my
$class_name
=
$associated_class
->name;
foreach
my
$handle
(
sort
keys
%handles
) {
my
$method_to_call
=
$handles
{
$handle
};
my
$name
=
"${class_name}::${handle}"
;
if
(
my
$method
=
$associated_class
->get_method(
$handle
) ) {
throw_exception(
CannotDelegateLocalMethodIsPresent
=>
attribute
=>
$self
,
method
=>
$method
,
)
unless
$method
->is_stub;
}
next
if
$class_name
->isa(
"Moose::Object"
)
and
$handle
=~ /^BUILD|DEMOLISH$/ || Moose::Object->can(
$handle
);
my
$method
=
$self
->_make_delegation_method(
$handle
,
$method_to_call
);
$self
->associated_class->add_method(
$method
->name,
$method
);
$self
->associate_method(
$method
);
}
}
sub
remove_delegation {
my
$self
=
shift
;
my
%handles
=
$self
->_canonicalize_handles;
my
$associated_class
=
$self
->associated_class;
foreach
my
$handle
(
keys
%handles
) {
next
unless
any {
$handle
eq
$_
}
map
{
$_
->name }
@{
$self
->associated_methods };
$self
->associated_class->remove_method(
$handle
);
}
}
sub
_canonicalize_handles {
my
$self
=
shift
;
my
$handles
=
$self
->handles;
if
(
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'
) {
(
$self
->has_type_constraint)
|| throw_exception(
CannotDelegateWithoutIsa
=>
attribute
=>
$self
);
return
map
{ (
$_
=>
$_
) }
grep
{ /
$handles
/ }
$self
->_get_delegate_method_list;
}
elsif
(
$handle_type
eq
'CODE'
) {
return
$handles
->(
$self
,
$self
->_find_delegate_metaclass);
}
elsif
(blessed(
$handles
) &&
$handles
->isa(
'Moose::Meta::TypeConstraint::DuckType'
)) {
return
map
{
$_
=>
$_
} @{
$handles
->methods };
}
elsif
(blessed(
$handles
) &&
$handles
->isa(
'Moose::Meta::TypeConstraint::Role'
)) {
$handles
=
$handles
->role;
}
else
{
throw_exception(
UnableToCanonicalizeHandles
=>
attribute
=>
$self
,
handles
=>
$handles
);
}
}
Moose::Util::_load_user_class(
$handles
);
my
$role_meta
= Class::MOP::class_of(
$handles
);
(blessed
$role_meta
&&
$role_meta
->isa(
'Moose::Meta::Role'
))
|| throw_exception(
UnableToCanonicalizeNonRolePackage
=>
attribute
=>
$self
,
handles
=>
$handles
);
return
map
{
$_
=>
$_
}
map
{
$_
->name }
grep
{ !
$_
->isa(
'Class::MOP::Method::Meta'
) } (
$role_meta
->_get_local_methods,
$role_meta
->get_required_method_list,
);
}
sub
_get_delegate_method_list {
my
$self
=
shift
;
my
$meta
=
$self
->_find_delegate_metaclass;
if
(
$meta
->isa(
'Class::MOP::Class'
)) {
return
map
{
$_
->name }
grep
{
$_
->package_name ne
'Moose::Object'
&& !
$_
->isa(
'Class::MOP::Method::Meta'
) }
$meta
->get_all_methods;
}
elsif
(
$meta
->isa(
'Moose::Meta::Role'
)) {
return
$meta
->get_method_list;
}
else
{
throw_exception(
UnableToRecognizeDelegateMetaclass
=>
attribute
=>
$self
,
delegate_metaclass
=>
$meta
);
}
}
sub
_find_delegate_metaclass {
my
$self
=
shift
;
my
$class
=
$self
->_isa_metadata;
my
$role
=
$self
->_does_metadata;
if
(
$class
) {
unless
(
$self
->type_constraint->isa(
"Moose::Meta::TypeConstraint::Class"
) ) {
throw_exception(
DelegationToATypeWhichIsNotAClass
=>
attribute
=>
$self
);
}
unless
( Moose::Util::_is_package_loaded(
$class
) ) {
throw_exception(
DelegationToAClassWhichIsNotLoaded
=>
attribute
=>
$self
,
class_name
=>
$class
);
}
return
Class::MOP::Class->initialize(
$class
);
}
elsif
(
$role
) {
unless
( Moose::Util::_is_package_loaded(
$role
) ) {
throw_exception(
DelegationToARoleWhichIsNotLoaded
=>
attribute
=>
$self
,
role_name
=>
$role
);
}
return
Class::MOP::class_of(
$role
);
}
else
{
throw_exception(
CannotFindDelegateMetaclass
=>
attribute
=>
$self
);
}
}
sub
delegation_metaclass {
'Moose::Meta::Method::Delegation'
}
sub
_make_delegation_method {
my
(
$self
,
$handle_name
,
$method_to_call
) =
@_
;
my
@curried_arguments
;
(
$method_to_call
,
@curried_arguments
) =
@$method_to_call
if
'ARRAY'
eq
ref
(
$method_to_call
);
return
$self
->delegation_metaclass->new(
name
=>
$handle_name
,
package_name
=>
$self
->associated_class->name,
attribute
=>
$self
,
delegate_to_method
=>
$method_to_call
,
curried_arguments
=> \
@curried_arguments
,
);
}
sub
_coerce_and_verify {
my
$self
=
shift
;
my
$val
=
shift
;
my
$instance
=
shift
;
return
$val
unless
$self
->has_type_constraint;
$val
=
$self
->type_constraint->coerce(
$val
)
if
$self
->should_coerce &&
$self
->type_constraint->has_coercion;
$self
->verify_against_type_constraint(
$val
,
instance
=>
$instance
);
return
$val
;
}
sub
verify_against_type_constraint {
my
$self
=
shift
;
my
$val
=
shift
;
return
1
if
!
$self
->has_type_constraint;
my
$type_constraint
=
$self
->type_constraint;
$type_constraint
->check(
$val
)
|| throw_exception(
ValidationFailedForTypeConstraint
=>
type
=>
$type_constraint
,
value
=>
$val
,
attribute
=>
$self
,
);
}
package
Moose::Meta::Attribute::Custom::Moose;
sub
register_implementation {
'Moose::Meta::Attribute'
}
1;