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;
use overload ();
-our $VERSION = '1.19';
-our $AUTHORITY = 'cpan:STEVAN';
-
use Moose::Deprecated;
use Moose::Meta::Method::Accessor;
use Moose::Meta::Method::Delegation;
__PACKAGE__->meta->add_attribute('traits' => (
reader => 'applied_traits',
predicate => 'has_applied_traits',
+ Class::MOP::_definition_context(),
));
# we need to have a ->does method in here to
return $self->Moose::Object::does($name);
}
+sub _error_thrower {
+ my $self = shift;
+ require Moose::Meta::Class;
+ ( ref $self && $self->associated_class ) || "Moose::Meta::Class";
+}
+
sub throw_error {
my $self = shift;
- my $class = ( ref $self && $self->associated_class ) || "Moose::Meta::Class";
- unshift @_, "message" if @_ % 2 == 1;
- unshift @_, attr => $self if ref $self;
- unshift @_, $class;
- my $handler = $class->can("throw_error"); # to avoid incrementing depth by 1
- goto $handler;
+ Moose::Util::throw(@_);
}
sub _inline_throw_error {
my ( $self, $msg, $args ) = @_;
- "\$meta->throw_error($msg" . ($args ? ", $args" : "") . ")"; # FIXME makes deparsing *REALLY* hard
+
+ my $inv = $self->_error_thrower;
+ # XXX ugh
+ $inv = 'Moose::Meta::Class' unless $inv->can('_inline_throw_error');
+
+ return $inv->_inline_throw_error($msg, $args)
}
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 (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;
}
}
my @found_illegal_options = grep { exists $options{$_} && exists $self->{$_} ? $_ : undef } @illegal_options;
(scalar @found_illegal_options == 0)
- || $self->throw_error("Illegal inherited options => (" . (join ', ' => @found_illegal_options) . ")", data => \%options);
+ || $self->throw_error(message => "Illegal inherited options => (" . (join ', ' => @found_illegal_options) . ")", data => \%options);
if ($options{isa}) {
my $type_constraint;
$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});
+ || $self->throw_error(message => "Could not find the type constraint '" . $options{isa} . "'", data => $options{isa});
}
$options{type_constraint} = $type_constraint;
$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});
+ || $self->throw_error(message => "Could not find the type constraint '" . $options{does} . "'", data => $options{does});
}
$options{type_constraint} = $type_constraint;
if ( $options->{is} eq 'ro' ) {
$class->throw_error(
- "Cannot define an accessor name on a read-only attribute, accessors are read/write",
+ message => "Cannot define an accessor name on a read-only attribute, accessors are read/write",
data => $options )
if exists $options->{accessor};
$options->{reader} ||= $name;
# do nothing, but don't complain (later) about missing methods
}
else {
- $class->throw_error( "I do not understand this option (is => "
- . $options->{is}
- . ") on attribute ($name)", data => $options->{is} );
+ $class->throw_error(
+ message => "I do not understand this option (is => "
+ . $options->{is}
+ . ") on attribute ($name)", data => $options->{is}
+ );
}
}
if ( try { $options->{isa}->can('does') } ) {
( $options->{isa}->does( $options->{does} ) )
|| $class->throw_error(
- "Cannot have an isa option and a does option if the isa does not do the does on attribute ($name)",
+ message => "Cannot have an isa option and a does option if the isa does not do the does on attribute ($name)",
data => $options );
}
else {
$class->throw_error(
- "Cannot have an isa option which cannot ->does() on attribute ($name)",
+ message => "Cannot have an isa option which cannot ->does() on attribute ($name)",
data => $options );
}
}
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} }
+ );
}
}
( exists $options->{type_constraint} )
|| $class->throw_error(
- "You cannot have coercion without specifying a type constraint on attribute ($name)",
+ message => "You cannot have coercion without specifying a type constraint on attribute ($name)",
data => $options );
$class->throw_error(
- "You cannot have a weak reference to a coerced value on attribute ($name)",
+ message => "You cannot have a weak reference to a coerced value on attribute ($name)",
data => $options )
if $options->{weak_ref};
return unless exists $options->{trigger};
( 'CODE' eq ref $options->{trigger} )
- || $class->throw_error("Trigger must be a CODE ref on attribute ($name)", data => $options->{trigger});
+ || $class->throw_error(message => "Trigger must be a CODE ref on attribute ($name)", data => $options->{trigger});
}
sub _process_auto_deref_option {
( exists $options->{type_constraint} )
|| $class->throw_error(
- "You cannot auto-dereference without specifying a type constraint on attribute ($name)",
+ message => "You cannot auto-dereference without specifying a type constraint on attribute ($name)",
data => $options );
( $options->{type_constraint}->is_a_type_of('ArrayRef')
|| $options->{type_constraint}->is_a_type_of('HashRef') )
|| $class->throw_error(
- "You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)",
+ message => "You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)",
data => $options );
}
return unless $options->{lazy_build};
$class->throw_error(
- "You can not use lazy_build and default for the same attribute ($name)",
+ message => "You can not use lazy_build and default for the same attribute ($name)",
data => $options )
if exists $options->{default};
( exists $options->{default} || defined $options->{builder} )
|| $class->throw_error(
- "You cannot have a lazy attribute ($name) without specifying a default value for it",
+ message => "You cannot have a lazy attribute ($name) without specifying a default value for it",
data => $options );
}
)
) {
$class->throw_error(
- "You cannot have a required attribute ($name) without a default, builder, or an init_arg",
+ message => "You cannot have a required attribute ($name) without a default, builder, or an init_arg",
data => $options );
}
}
# skip it if it's lazy
return if $self->is_lazy;
# and die if it's required and doesn't have a default value
- $self->throw_error("Attribute (" . $self->name . ") is required", object => $instance, data => $params)
+ $self->throw_error(message => "Attribute (" . $self->name . ") is required", object => $instance, data => $params)
if $self->is_required && !$self->has_default && !$self->has_builder;
# if nothing was in the %params, we can use the
return $instance->$builder()
if $instance->can( $self->builder );
- $self->throw_error( blessed($instance)
- . " does not support builder method '"
- . $self->builder
- . "' for attribute '"
- . $self->name
- . "'",
+ $self->throw_error(
+ message => blessed($instance)
+ . " does not support builder method '"
+ . $self->builder
+ . "' for attribute '"
+ . $self->name
+ . "'",
object => $instance,
);
}
my ($self, $instance, @args) = @_;
my $value = $args[0];
- my $attr_name = $self->name;
+ my $attr_name = quotemeta($self->name);
if ($self->is_required and not @args) {
- $self->throw_error("Attribute ($attr_name) is required", object => $instance);
+ $self->throw_error(message => "Attribute ($attr_name) is required", object => $instance);
}
$value = $self->_coerce_and_verify( $value, $instance );
sub _inline_set_value {
my $self = shift;
- my ($instance, $value, $tc, $tc_obj, $for_constructor) = @_;
+ my ($instance, $value, $tc, $coercion, $message, $for_constructor) = @_;
- my $old = '@old';
- my $copy = '$val';
- $tc ||= '$type_constraint';
- $tc_obj ||= '$type_constraint_obj';
+ 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_check_required
unless $for_constructor;
- push @code, $self->_inline_tc_code($value, $tc, $tc_obj);
+ push @code, $self->_inline_tc_code($value, $tc, $coercion, $message);
# constructors do triggers all at once at the end
push @code, $self->_inline_get_old_value_for_trigger($instance, $old)
sub _inline_tc_code {
my $self = shift;
+ my ($value, $tc, $coercion, $message, $is_lazy) = @_;
return (
- $self->_inline_check_coercion(@_),
- $self->_inline_check_constraint(@_),
+ $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, $tc_obj) = @_;
+ my ($value, $tc, $coercion) = @_;
return unless $self->should_coerce && $self->type_constraint->has_coercion;
- return $value . ' = ' . $tc_obj . '->coerce(' . $value . ');';
+ 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, $tc_obj) = @_;
+ my ($value, $tc, $message) = @_;
return unless $self->has_type_constraint;
my $attr_name = quotemeta($self->name);
- return (
- 'if (!' . $tc . '->(' . $value . ')) {',
- $self->_inline_throw_error(
- '"Attribute (' . $attr_name . ') does not pass the type '
- . 'constraint because: " . '
- . $tc_obj . '->get_message(' . $value . ')',
- 'data => ' . $value
- ) . ';',
- '}',
- );
+ if ( $self->type_constraint->can_be_inlined ) {
+ return (
+ 'if (! (' . $self->type_constraint->_inline_check($value) . ')) {',
+ $self->_inline_throw_error(
+ '"Attribute (' . $attr_name . ') does not pass the type '
+ . 'constraint because: " . '
+ . 'do { local $_ = ' . $value . '; '
+ . $message . '->(' . $value . ')'
+ . '}',
+ 'data => ' . $value
+ ) . ';',
+ '}',
+ );
+ }
+ else {
+ return (
+ 'if (!' . $tc . '->(' . $value . ')) {',
+ $self->_inline_throw_error(
+ '"Attribute (' . $attr_name . ') does not pass the type '
+ . 'constraint because: " . '
+ . 'do { local $_ = ' . $value . '; '
+ . $message . '->(' . $value . ')'
+ . '}',
+ 'data => ' . $value
+ ) . ';',
+ '}',
+ );
+ }
}
sub _inline_get_old_value_for_trigger {
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 unless $self->has_trigger;
- return '$attr->trigger->(' . $instance . ', ' . $value . ', ' . $old . ');';
+ 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 {
$value = $self->_coerce_and_verify( $value, $instance );
$self->set_initial_value($instance, $value);
+
+ if ( ref $value && $self->is_weak_ref ) {
+ $self->_weaken_value($instance);
+ }
}
}
return wantarray ? %{ $rv } : $rv;
}
else {
- $self->throw_error("Can not auto de-reference the type constraint '" . $type_constraint->name . "'", object => $instance, type_constraint => $type_constraint);
+ $self->throw_error(message => "Can not auto de-reference the type constraint '" . $type_constraint->name . "'", object => $instance, type_constraint => $type_constraint);
}
}
sub _inline_get_value {
my $self = shift;
- my ($instance, $tc, $tc_obj) = @_;
+ my ($instance, $tc, $coercion, $message) = @_;
my $slot_access = $self->_inline_instance_get($instance);
$tc ||= '$type_constraint';
- $tc_obj ||= '$type_constraint_obj';
+ $coercion ||= '$type_coercion';
+ $message ||= '$type_message';
return (
- $self->_inline_check_lazy($instance, $tc, $tc_obj),
+ $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, $tc_obj) = @_;
+ my ($instance, $tc, $coercion, $message) = @_;
return unless $self->is_lazy;
return (
'if (!' . $slot_exists . ') {',
- $self->_inline_init_from_default($instance, '$default', $tc, $tc_obj, 'lazy'),
+ $self->_inline_init_from_default($instance, '$default', $tc, $coercion, $message, 'lazy'),
'}',
);
}
sub _inline_init_from_default {
my $self = shift;
- my ($instance, $default, $tc, $tc_obj, $for_lazy) = @_;
+ my ($instance, $default, $tc, $coercion, $message, $for_lazy) = @_;
if (!($self->has_default || $self->has_builder)) {
$self->throw_error(
- 'You cannot have a lazy attribute '
+ message => 'You cannot have a lazy attribute '
. '(' . $self->name . ') '
. 'without specifying a default value for it',
attr => $self,
# to do things like possibly only do member tc checks, which isn't
# appropriate for checking the result of a default
$self->has_type_constraint
- ? ($self->_inline_check_coercion($default, $tc, $tc_obj, $for_lazy),
- $self->_inline_check_constraint($default, $tc, $tc_obj, $for_lazy))
+ ? ($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),
);
}
my ($instance, $default) = @_;
if ($self->has_default) {
- return 'my ' . $default . ' = $attr->default(' . $instance . ');';
+ 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($attr->builder)) {',
+ 'if (my $builder = ' . $instance . '->can(' . $builder . ')) {',
$default . ' = ' . $instance . '->$builder;',
'}',
'else {',
'my $class = ref(' . $instance . ') || ' . $instance . ';',
- 'my $builder_name = $attr->builder;',
- 'my $attr_name = $attr->name;',
$self->_inline_throw_error(
'"$class does not support builder method '
- . '\'$builder_name\' for attribute \'$attr_name\'"'
+ . '\'' . $builder_str . '\' for attribute '
+ . '\'' . $attr_name_str . '\'"'
) . ';',
'}',
);
}
else {
$self->throw_error(
- "Can't generate a default for " . $self->name
+ message => "Can't generate a default for " . $self->name
. " since no default or builder was specified"
);
}
}
else {
$self->throw_error(
- 'Can not auto de-reference the type constraint \''
+ message => 'Can not auto de-reference the type constraint \''
. $type_constraint->name
. '\'',
type_constraint => $type_constraint,
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(
+ message => "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
}
elsif ($handle_type eq 'Regexp') {
($self->has_type_constraint)
- || $self->throw_error("Cannot delegate methods based on a Regexp without a type constraint (isa)", data => $handles);
+ || $self->throw_error(message => "Cannot delegate methods based on a Regexp without a type constraint (isa)", data => $handles);
return map { ($_ => $_) }
grep { /$handles/ } $self->_get_delegate_method_list;
}
$handles = $handles->role;
}
else {
- $self->throw_error("Unable to canonicalize the 'handles' option with $handles", data => $handles);
+ $self->throw_error(message => "Unable to canonicalize the 'handles' option with $handles", data => $handles);
}
}
- 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'))
- || $self->throw_error("Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
+ || $self->throw_error(message => "Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
return map { $_ => $_ }
map { $_->name }
return $meta->get_method_list;
}
else {
- $self->throw_error("Unable to recognize the delegate metaclass '$meta'", data => $meta);
+ $self->throw_error(message => "Unable to recognize the delegate metaclass '$meta'", data => $meta);
}
}
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(
+ message => sprintf(
'The %s attribute is trying to delegate to a class which has not been loaded - %s',
$self->name, $class
)
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(
+ message => sprintf(
'The %s attribute is trying to delegate to a role which has not been loaded - %s',
$self->name, $role
)
return Class::MOP::class_of($role);
}
else {
- $self->throw_error("Cannot find delegate metaclass for attribute " . $self->name);
+ $self->throw_error(message => "Cannot find delegate metaclass for attribute " . $self->name);
}
}
1;
+# ABSTRACT: The Moose attribute metaclass
+
__END__
=pod
-=head1 NAME
-
-Moose::Meta::Attribute - The Moose attribute metaclass
-
=head1 DESCRIPTION
This class is a subclass of L<Class::MOP::Attribute> that provides
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
See L<Moose/BUGS> for details on reporting bugs.
-=head1 AUTHOR
-
-Stevan Little E<lt>stevan@iinteractive.comE<gt>
-
-Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright 2006-2010 by Infinity Interactive, Inc.
-
-L<http://www.iinteractive.com>
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
=cut