use strict;
use warnings;
-use Class::MOP ();
use B ();
+use Class::Load qw(is_class_loaded load_class);
use Scalar::Util 'blessed', 'weaken';
use List::MoreUtils 'any';
use Try::Tiny;
sub new {
my ($class, $name, %options) = @_;
$class->_process_options($name, \%options) unless $options{__hack_no_process_options}; # used from clone()... YECHKKK FIXME ICKY YUCK GROSS
-
+
delete $options{__hack_no_process_options};
my %attrs =
if (@bad)
{
- Carp::cluck "Found unknown argument(s) passed to '$name' attribute constructor in '$class': @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);
if (my $traits = $options->{traits}) {
my $i = 0;
+ my $has_foreign_options = 0;
+
while ($i < @$traits) {
my $trait = $traits->[$i++];
next if ref($trait); # options to a trait we discarded
push @traits, $trait;
# are there options?
- push @traits, $traits->[$i++]
- if $traits->[$i] && ref($traits->[$i]);
+ 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 $anon_class = Moose::Meta::Class->create_anon_class(
+ my %options = (
superclasses => [ $class ],
roles => [ @traits ],
- cache => 1,
);
+ 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;
}
}
$type_constraint = $options{isa};
}
else {
- $type_constraint = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options{isa});
+ $type_constraint = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options{isa}, { package_defined_in => $options{definition_context}->{package} });
(defined $type_constraint)
|| $self->throw_error("Could not find the type constraint '" . $options{isa} . "'", data => $options{isa});
}
$type_constraint = $options{does};
}
else {
- $type_constraint = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options{does});
+ $type_constraint = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options{does}, { package_defined_in => $options{definition_context}->{package} });
(defined $type_constraint)
|| $self->throw_error("Could not find the type constraint '" . $options{does} . "'", data => $options{does});
}
else {
$options->{type_constraint}
= Moose::Util::TypeConstraints::find_or_create_isa_type_constraint(
- $options->{isa} );
+ $options->{isa},
+ { package_defined_in => $options->{definition_context}->{package} }
+ );
}
}
else {
$options->{type_constraint}
= Moose::Util::TypeConstraints::find_or_create_does_type_constraint(
- $options->{does} );
+ $options->{does},
+ { package_defined_in => $options->{definition_context}->{package} }
+ );
}
}
return (
'if (@_ < 2) {',
$self->_inline_throw_error(
- '"Attribute (' . $attr_name . ') is required, so cannot '
- . 'be set to undef"' # defined $_[1] is not good enough
+ '"Attribute (' . $attr_name . ') is required"'
) . ';',
'}',
);
my $mi = $self->associated_class->get_meta_instance;
return (
- $mi->inline_weaken_slot_value($instance, $self->name, $value),
+ $mi->inline_weaken_slot_value($instance, $self->name),
'if ref ' . $value . ';',
);
}
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;
+ # these two could probably get inlined versions too
+ $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 } };
+ }
+
+ # XXX ugh, fix these
+ $env->{'$attr'} = \$self
+ if $self->has_initializer && $self->is_lazy;
+ # pretty sure this is only going to be closed over if you use a custom
+ # error class at this point, but we should still get rid of this
+ # at some point
+ $env->{'$meta'} = \($self->associated_class);
+
+ return $env;
+}
+
sub _weaken_value {
my ( $self, $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);
+ }
}
}
$self->_inline_check_constraint($default, $tc, $message, $for_lazy))
: (),
$self->_inline_init_slot($instance, $default),
+ $self->_inline_weaken_value($instance, $default),
);
}
my ($instance, $default) = @_;
if ($self->has_default) {
- my $source = 'my ' . $default . ' = $default';
+ my $source = 'my ' . $default . ' = $attr_default';
$source .= '->(' . $instance . ')'
if $self->is_default_a_coderef;
return $source . ';';
if (
$method
+ && !$method->is_stub
&& !$method->isa('Class::MOP::Method::Accessor')
&& ( !$self->definition_context
|| $method->package_name eq $self->definition_context->{package} )
# install the delegation ...
my $associated_class = $self->associated_class;
- foreach my $handle (keys %handles) {
+ foreach my $handle (sort keys %handles) {
my $method_to_call = $handles{$handle};
my $class_name = $associated_class->name;
my $name = "${class_name}::${handle}";
- (!$associated_class->has_method($handle))
- || $self->throw_error("You cannot overwrite a locally defined method ($handle) with a delegation", method_name => $handle);
+ if ( my $method = $associated_class->get_method($handle) ) {
+ $self->throw_error(
+ "You cannot overwrite a locally defined method ($handle) with a delegation",
+ method_name => $handle
+ ) unless $method->is_stub;
+ }
# NOTE:
# handles is not allowed to delegate
}
}
- Class::MOP::load_class($handles);
+ load_class($handles);
my $role_meta = Class::MOP::class_of($handles);
(blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
sub _find_delegate_metaclass {
my $self = shift;
if (my $class = $self->_isa_metadata) {
- unless ( Class::MOP::is_class_loaded($class) ) {
+ unless ( is_class_loaded($class) ) {
$self->throw_error(
sprintf(
'The %s attribute is trying to delegate to a class which has not been loaded - %s',
return Class::MOP::Class->initialize($class);
}
elsif (my $role = $self->_does_metadata) {
- unless ( Class::MOP::is_class_loaded($class) ) {
+ unless ( is_class_loaded($class) ) {
$self->throw_error(
sprintf(
'The %s attribute is trying to delegate to a role which has not been loaded - %s',
=over 4
-=item B<< Moose::Meta::Attribute->new(%options) >>
+=item B<< Moose::Meta::Attribute->new($name, %options) >>
This method overrides the L<Class::MOP::Attribute> constructor.
Before setting the value, a check is made on the type constraint of
the attribute, if it has one, to see if the value passes it. If the
-value fails to pass, the set operation dies with a L</throw_error>.
+value fails to pass, the set operation dies.
Any coercion to convert values is done before checking the type constraint.
To check a value against a type constraint before setting it, fetch the
attribute instance using L<Class::MOP::Class/find_attribute_by_name>,
fetch the type_constraint from the attribute using L<Moose::Meta::Attribute/type_constraint>
-and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::Basics::Recipe4>
+and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::Basics::Company_Subtypes>
for an example.
=back