X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMouse%2FMeta%2FMethod%2FAccessor.pm;h=621a259b9c7ee61f7c483faefa3122ce2270e19b;hb=2a464664052830d5fad036569d5ccb3964c7f592;hp=ba46cbd4dc3f91d2b8607cfcca56013b4e425197;hpb=1ae8a0d7aaaa0627218d5026a013fb70113cce5a;p=gitmo%2FMouse.git diff --git a/lib/Mouse/Meta/Method/Accessor.pm b/lib/Mouse/Meta/Method/Accessor.pm old mode 100644 new mode 100755 index ba46cbd..621a259 --- a/lib/Mouse/Meta/Method/Accessor.pm +++ b/lib/Mouse/Meta/Method/Accessor.pm @@ -1,11 +1,10 @@ package Mouse::Meta::Method::Accessor; use strict; use warnings; -use Carp (); +use Scalar::Util qw(blessed); -# internal use only. do not call directly -sub generate_accessor_method_inline { - my ($class, $attribute) = @_; +sub _generate_accessor{ + my (undef, $attribute, $method_name, $class, $type) = @_; my $name = $attribute->name; my $default = $attribute->default; @@ -16,47 +15,49 @@ sub generate_accessor_method_inline { my $should_deref = $attribute->should_auto_deref; my $should_coerce = $attribute->should_coerce; - my $compiled_type_constraint = $constraint ? $constraint->{_compiled_type_constraint} : undef; + my $compiled_type_constraint = $constraint ? $constraint->_compiled_type_constraint : undef; my $self = '$_[0]'; - my $key = $attribute->inlined_name; + my $key = sprintf q{"%s"}, quotemeta $name; + + $type ||= 'accessor'; my $accessor = '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - "sub {\n"; - if ($attribute->_is_metadata eq 'rw') { - $accessor .= - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'if (@_ >= 2) {' . "\n"; + sprintf("sub %s {\n", defined($method_name) ? $class->name . '::' . $method_name : ''); + if ($type eq 'accessor' || $type eq 'writer') { + if($type eq 'accessor'){ + $accessor .= + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'if (scalar(@_) >= 2) {' . "\n"; + } + else{ # writer + $accessor .= + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'if(@_ < 2){ Carp::confess("Not enough arguments for the writer of '.$name.'") }'. + '{' . "\n"; + } + my $value = '$_[1]'; - if ($constraint) { - $accessor .= 'my $val = '; + if (defined $constraint) { + if(!$compiled_type_constraint){ + Carp::confess("[BUG] Missing compiled type constraint for $constraint"); + } if ($should_coerce) { $accessor .= "\n". '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'Mouse::Util::TypeConstraints->typecast_constraints("'.$attribute->associated_class->name.'", $attribute->{type_constraint}, '.$value.');'; - } else { - $accessor .= $value.';'; - } - if ($compiled_type_constraint) { - $accessor .= - "\n". - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'unless ($compiled_type_constraint->($val)) { - $attribute->verify_type_constraint_error($name, $val, $attribute->{type_constraint}); - }' . "\n"; - } else { - $accessor .= - "\n". - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'unless ($constraint->check($val)) { - $attribute->verify_type_constraint_error($name, $val, $attribute->{type_constraint}); - }' . "\n"; + 'my $val = $constraint->coerce('.$value.');'; + $value = '$val'; } - $value = '$val'; + $accessor .= + "\n". + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'unless ($compiled_type_constraint->('.$value.')) { + $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint}); + }' . "\n"; } # if there's nothing left to do for the attribute we can return during @@ -75,40 +76,124 @@ sub generate_accessor_method_inline { $accessor .= "}\n"; } - else { + elsif($type eq 'reader') { $accessor .= 'Carp::confess("Cannot assign a value to a read-only accessor") if scalar(@_) >= 2;' . "\n"; } + else{ + $class->throw_error("Unknown accessor type '$type'"); + } if ($attribute->is_lazy) { $accessor .= $self.'->{'.$key.'} = '; - $accessor .= $attribute->has_builder - ? $self.'->$builder' - : ref($default) eq 'CODE' - ? '$default->('.$self.')' - : '$default'; + if($should_coerce && defined($constraint)){ + $accessor .= '$attribute->_coerce_and_verify('; + } + $accessor .= $attribute->has_builder ? $self.'->$builder' + : ref($default) eq 'CODE' ? '$default->('.$self.')' + : '$default'; + + if($should_coerce && defined $constraint){ + $accessor .= ')'; + } $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n"; } if ($should_deref) { - if (ref($constraint) && $constraint->name eq 'ArrayRef') { + if ($constraint->is_a_type_of('ArrayRef')) { $accessor .= 'if (wantarray) { return @{ '.$self.'->{'.$key.'} || [] }; }'; } - else { + elsif($constraint->is_a_type_of('HashRef')){ $accessor .= 'if (wantarray) { return %{ '.$self.'->{'.$key.'} || {} }; }'; } + else{ + $class->throw_error("Can not auto de-reference the type constraint " . $constraint->name); + } } - $accessor .= 'return '.$self.'->{'.$key.'}; - }'; + $accessor .= 'return '.$self.'->{'.$key."};\n}"; + + #print $accessor, "\n"; + my $code; + my $e = do{ + local $@; + $code = eval $accessor; + $@; + }; + die $e if $e; + + return $code; # returns a CODE ref unless $method_name is passed +} + +sub _generate_reader{ + my $class = shift; + return $class->_generate_accessor(@_, 'reader'); +} + +sub _generate_writer{ + my $class = shift; + return $class->_generate_accessor(@_, 'writer'); +} + + +sub _generate_predicate { + my (undef, $attribute, $method_name, $class) = @_; + + my $slot = $attribute->name; - my $sub = eval $accessor; - Carp::confess($@) if $@; - return $sub; + $class->add_method($method_name => sub{ + return exists $_[0]->{$slot}; + }); + return; } +sub _generate_clearer { + my (undef, $attribute, $method_name, $class) = @_; + + my $slot = $attribute->name; + + $class->add_method($method_name => sub{ + delete $_[0]->{$slot}; + }); + return; +} + +sub _generate_handles { + my (undef, $attribute, $handles, $class) = @_; + + my $reader = $attribute->reader || $attribute->accessor + or $class->throw_error("You must pass a reader method for '".$attribute->name."'"); + + my %handles = $attribute->_canonicalize_handles($handles); + + foreach my $handle_name (keys %handles) { + my $method_to_call = $handles{$handle_name}; + + my $code = sub { + my $instance = shift; + my $proxy = $instance->$reader(); + + my $error = !defined($proxy) ? ' is not defined' + : ref($proxy) && !blessed($proxy) ? qq{ is not an object (got '$proxy')} + : undef; + if ($error) { + $instance->meta->throw_error( + "Cannot delegate $handle_name to $method_to_call because " + . "the value of " + . $attribute->name + . $error + ); + } + $proxy->$method_to_call(@_); + }; + $class->add_method($handle_name => $code); + } + return; +} + + 1;