use base 'Class::MOP::Attribute';
-__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
-__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
-__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce'));
-__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
+__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
+__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
+__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
+__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
+__PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
__PACKAGE__->meta->add_attribute('type_constraint' => (
reader => 'type_constraint',
predicate => 'has_type_constraint',
return $mi->inline_get_slot_value($instance, $slot_name);
}
+sub _inline_auto_deref {
+ my ( $self, $ref_value ) = @_;
+
+ return $ref_value unless $self->should_auto_deref;
+
+ my $type = eval { $self->type_constraint->name } || '';
+ my $sigil;
+
+ if ( $type eq "ArrayRef" ) {
+ $sigil = '@';
+ } elsif ( $type eq 'HashRef' ) {
+ $sigil = '%';
+ } else {
+ confess "Can't auto deref unless type constraint is ArrayRef or HashRef";
+ }
+
+ "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
+}
+
sub generate_accessor_method {
my ($attr, $attr_name) = @_;
my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
'$_[0]->{$attr_name} = ($attr->has_default ? $attr->default($_[0]) : undef)'
. 'unless exists $_[0]->{$attr_name};'
: '')
- . 'return ' . $attr->_inline_get( $inv )
+ . 'return ' . $attr->_inline_auto_deref( $attr->_inline_get( $inv ) )
. ' }';
my $sub = eval $code;
warn "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
'$_[0]->{$attr_name} = ($self->has_default ? $self->default($_[0]) : undef)'
. 'unless exists $_[0]->{$attr_name};'
: '')
- . 'return $_[0]->{$attr_name};'
+ . 'return ' . $self->_inline_auto_deref( '$_[0]->{$attr_name}' ) . ';'
. '}';
my $sub = eval $code;
confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
use strict;
use warnings;
-use Test::More tests => 33;
+use Test::More tests => 57;
use Test::Exception;
use Scalar::Util 'isweak';
);
};
::ok(!$@, '... created the accessor method with weak_ref okay');
+
+ eval {
+ has 'foo_deref' => (
+ accessor => 'foo_deref',
+ isa => 'ArrayRef',
+ auto_deref => 1,
+ );
+ };
+ ::ok(!$@, '... created the accessor method with auto_deref okay');
+
+ eval {
+ has 'foo_deref_ro' => (
+ reader => 'foo_deref_ro',
+ isa => 'ArrayRef',
+ auto_deref => 1,
+ );
+ };
+ ::ok(!$@, '... created the reader method with auto_deref okay');
+
+ eval {
+ has 'foo_deref_hash' => (
+ accessor => 'foo_deref_hash',
+ isa => 'HashRef',
+ auto_deref => 1,
+ );
+ };
+ ::ok(!$@, '... created the reader method with auto_deref okay');
}
{
ok(isweak($foo->{foo_weak}), '... it is a weak reference');
+ can_ok( $foo, 'foo_deref');
+ is( $foo->foo_deref(), undef, '... unset value');
+ my @list;
+ lives_ok {
+ @list = $foo->foo_deref();
+ } "... doesn't deref undef value";
+ is_deeply( \@list, [], "returns empty list in list context");
+
+ lives_ok {
+ $foo->foo_deref( [ qw/foo bar gorch/ ] );
+ } '... foo_deref wrote successfully';
+
+ is( Scalar::Util::reftype( scalar $foo->foo_deref() ), "ARRAY", "returns an array reference in scalar context" );
+ is_deeply( scalar($foo->foo_deref()), [ qw/foo bar gorch/ ], "correct array" );
+
+ is( scalar( () = $foo->foo_deref() ), 3, "returns list in list context" );
+ is_deeply( [ $foo->foo_deref() ], [ qw/foo bar gorch/ ], "correct list" );
+
+
+ can_ok( $foo, 'foo_deref' );
+ is( $foo->foo_deref_ro(), undef, "... unset value" );
+
+ dies_ok {
+ $foo->foo_deref_ro( [] );
+ } "... read only";
+
+ $foo->{foo_deref_ro} = [qw/la la la/];
+
+ is_deeply( scalar($foo->foo_deref_ro()), [qw/la la la/], "scalar context ro" );
+ is_deeply( [ $foo->foo_deref_ro() ], [qw/la la la/], "list context ro" );
+
+ can_ok( $foo, 'foo_deref_hash' );
+ is( $foo->foo_deref_hash(), undef, "... unset value" );
+
+ my %hash;
+ lives_ok {
+ %hash = $foo->foo_deref_hash();
+ } "... doesn't deref undef value";
+ is_deeply( \%hash, {}, "returns empty list in list context");
+
+ lives_ok {
+ $foo->foo_deref_hash( { foo => 1, bar => 2 } );
+ } '... foo_deref_hash wrote successfully';
+
+ is_deeply( scalar($foo->foo_deref_hash), { foo => 1, bar => 2 }, "scalar context" );
+
+ %hash = $foo->foo_deref_hash;
+ is_deeply( \%hash, { foo => 1, bar => 2 }, "list context");
}