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.17';
-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";
+ my $inv = $self->_error_thrower;
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
+ unshift @_, $inv;
+ my $handler = $inv->can("throw_error"); # to avoid incrementing depth by 1
goto $handler;
}
+sub _inline_throw_error {
+ my ( $self, $msg, $args ) = @_;
+
+ my $inv = $self->_error_thrower;
+ # XXX ugh
+ $inv = 'Moose::Meta::Class' unless $inv->can('_inline_throw_error');
+
+ # XXX ugh ugh UGH
+ my $class = $self->associated_class;
+ if ($class) {
+ my $class_name = B::perlstring($class->name);
+ my $attr_name = B::perlstring($self->name);
+ $args = 'attr => Class::MOP::class_of(' . $class_name . ')'
+ . '->find_attribute_by_name(' . $attr_name . '), '
+ . (defined $args ? $args : '');
+ }
+
+ 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;
}
}
## Slot management
-# FIXME:
-# this duplicates too much code from
-# Class::MOP::Attribute, we need to
-# refactor these bits eventually.
-# - SL
-sub _set_initial_slot_value {
- my ($self, $meta_instance, $instance, $value) = @_;
-
- my $slot_name = $self->name;
-
- return $meta_instance->set_slot_value($instance, $slot_name, $value)
- unless $self->has_initializer;
-
- my $callback = sub {
- my $val = $self->_coerce_and_verify( shift, $instance );;
-
- $meta_instance->set_slot_value($instance, $slot_name, $val);
+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));
};
-
- my $initializer = $self->initializer;
-
- # most things will just want to set a value, so make it first arg
- $instance->$initializer($value, $callback, $self);
}
sub set_value {
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);
}
}
+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;
+ }
+
+ # constructors already handle required checks
+ push @code, $self->_inline_check_required
+ unless $for_constructor;
+
+ 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)
+ unless $for_constructor;
+
+ push @code, (
+ $self->SUPER::_inline_set_value($instance, $value),
+ $self->_inline_weaken_value($instance, $value),
+ );
+
+ # constructors do triggers all at once at the end
+ 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 $attr_name = quotemeta($self->name);
+
+ return (
+ 'if (@_ < 2) {',
+ $self->_inline_throw_error(
+ '"Attribute (' . $attr_name . ') is required, so cannot '
+ . 'be set to undef"' # defined $_[1] is not good enough
+ ) . ';',
+ '}',
+ );
+}
+
+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) . ')) {',
+ $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 $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, $value),
+ '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;
+ # 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 ) = @_;
}
}
+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)) {
+ $self->throw_error(
+ 'You cannot have a lazy attribute '
+ . '(' . $self->name . ') '
+ . 'without specifying a default value for it',
+ attr => $self,
+ );
+ }
+
+ return (
+ $self->_inline_generate_default($instance, $default),
+ # intentionally not using _inline_tc_code, since that can be overridden
+ # 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, $coercion, $for_lazy),
+ $self->_inline_check_constraint($default, $tc, $message, $for_lazy))
+ : (),
+ $self->_inline_init_slot($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_error(
+ '"$class does not support builder method '
+ . '\'' . $builder_str . '\' for attribute '
+ . '\'' . $attr_name_str . '\'"'
+ ) . ';',
+ '}',
+ );
+ }
+ else {
+ $self->throw_error(
+ "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 {
+ $self->throw_error(
+ 'Can not auto de-reference the type constraint \''
+ . $type_constraint->name
+ . '\'',
+ type_constraint => $type_constraint,
+ );
+ }
+
+ return 'wantarray '
+ . '? ' . $sigil . '{ (' . $ref_value . ') || return } '
+ . ': (' . $ref_value . ')';
+}
+
## installing accessors
sub accessor_metaclass { 'Moose::Meta::Method::Accessor' }
$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')
+ && $method->associated_attribute->name ne $self->name ) {
+
+ my $other_attr_name = $method->associated_attribute->name;
+ my $name = $self->name;
+
+ Carp::cluck(
+ "You are overwriting an accessor ($accessor) for the $other_attr_name attribute"
+ . " with a new accessor method for the $name attribute" );
+ }
+
if (
$method
&& !$method->isa('Class::MOP::Method::Accessor')
"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 ) ) {
return;
}
-sub inline_set {
- my $self = shift;
- my ( $instance, $value ) = @_;
-
- my $mi = $self->associated_class->get_meta_instance;
-
- my $code
- = $mi->inline_set_slot_value( $instance, $self->slots, $value ) . ";";
- $code
- .= $mi->inline_weaken_slot_value( $instance, $self->slots, $value )
- . " if ref $value;"
- if $self->is_weak_ref;
-
- return $code;
-}
-
sub install_delegation {
my $self = shift;
# 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}";
}
}
- 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',
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
predicate => 'has_size',
);
+
+If your attribute name starts with an underscore (C<_>), then the clearer
+and predicate will as well:
+
+ has '_size' => (
+ is => 'ro',
+ lazy_build => 1,
+ );
+
+becomes:
+
+ has '_size' => (
+ is => 'ro',
+ lazy => 1,
+ builder => '_build__size',
+ clearer => '_clear_size',
+ predicate => '_has_size',
+ );
+
+Note the doubled underscore in the builder name. Internally, Moose
+simply prepends the attribute name with "_build_" to come up with the
+builder name.
+
=item * documentation
An arbitrary string that can be retrieved later by calling C<<
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