From: gfx Date: Sun, 20 Sep 2009 03:06:58 +0000 (+0900) Subject: Merge branch 'master' into topic/more-compatible X-Git-Tag: 0.32~51 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=15b4faa9f83b2f8d62c4d875672ca801f0c89ed1;hp=10389a8bffa67d857a7ec8332d82beb681bcfb18;p=gitmo%2FMouse.git Merge branch 'master' into topic/more-compatible Conflicts: Changes --- diff --git a/Changes b/Changes index cb2f29f..fbf45aa 100644 --- a/Changes +++ b/Changes @@ -2,10 +2,13 @@ Revision history for Mouse 0.30 -0.29 Thu Sep 17 11:49:49 2009 + * Support is => 'bare', and you must pass the 'is' option (gfx) + + * Make generator methods private (gfx) - * role class has ->meta in method_list, because it does in Moose since 0.90 +0.29 Thu Sep 17 11:49:49 2009 + * role class has ->meta in method_list, because it does in Moose since 0.9 0.28 Wed Sep 8 20:00:06 2009 * Alter Makefile.PL so in author mode we generate lib/Mouse/Tiny.pm on every run so that 'make dist' actually does what it's meant to (mst) diff --git a/Makefile.PL b/Makefile.PL index 5f13219..aa7620c 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -65,7 +65,7 @@ sub create_moose_compatibility_test { my $dirname = File::Basename::dirname($_); my $tmpdir = File::Spec->catfile('xt', 'compatibility', $dirname); - File::Path::make_path($tmpdir); + File::Path::mkpath($tmpdir); my $tmpfile = File::Spec->catfile($tmpdir, $basename); open my $wfh, '>', $tmpfile or die $!; diff --git a/lib/Mouse.pm b/lib/Mouse.pm index 264f32c..7ca9997 100644 --- a/lib/Mouse.pm +++ b/lib/Mouse.pm @@ -122,7 +122,9 @@ sub init_meta { { no strict 'refs'; no warnings 'redefine'; - *{$class.'::meta'} = sub { $meta }; + *{$class.'::meta'} = sub { + return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]); + }; } return $meta; diff --git a/lib/Mouse/Meta/Attribute.pm b/lib/Mouse/Meta/Attribute.pm index dfae8b2..8abdd14 100644 --- a/lib/Mouse/Meta/Attribute.pm +++ b/lib/Mouse/Meta/Attribute.pm @@ -1,12 +1,10 @@ package Mouse::Meta::Attribute; use strict; use warnings; -require overload; use Carp 'confess'; use Scalar::Util (); use Mouse::Meta::TypeConstraint; -use Mouse::Meta::Method::Accessor; sub new { my ($class, $name, %options) = @_; @@ -56,16 +54,119 @@ sub _create_args { $_[0]->{_create_args} } -sub inlined_name { +sub _inlined_name { my $self = shift; - my $name = $self->name; - my $key = "'" . $name . "'"; - return $key; + return sprintf '"%s"', quotemeta $self->name; } -sub generate_predicate { +sub _generate_accessor{ + my ($attribute) = @_; + + my $name = $attribute->name; + my $default = $attribute->default; + my $constraint = $attribute->type_constraint; + my $builder = $attribute->builder; + my $trigger = $attribute->trigger; + my $is_weak = $attribute->is_weak_ref; + my $should_deref = $attribute->should_auto_deref; + my $should_coerce = $attribute->should_coerce; + + my $compiled_type_constraint = $constraint ? $constraint->{_compiled_type_constraint} : undef; + + my $self = '$_[0]'; + my $key = $attribute->_inlined_name; + + my $accessor = + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + "sub {\n"; + if ($attribute->_is_metadata eq 'rw') { + $accessor .= + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'if (scalar(@_) >= 2) {' . "\n"; + + my $value = '$_[1]'; + + if ($constraint) { + if ($should_coerce) { + $accessor .= + "\n". + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'my $val = Mouse::Util::TypeConstraints->typecast_constraints("'.$attribute->associated_class->name.'", $attribute->{type_constraint}, '.$value.');'; + $value = '$val'; + } + if ($compiled_type_constraint) { + $accessor .= + "\n". + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'unless ($compiled_type_constraint->('.$value.')) { + $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint}); + }' . "\n"; + } else { + $accessor .= + "\n". + '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . + 'unless ($constraint->check('.$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 + # this setter + $accessor .= 'return ' if !$is_weak && !$trigger && !$should_deref; + + $accessor .= $self.'->{'.$key.'} = '.$value.';' . "\n"; + + if ($is_weak) { + $accessor .= 'Scalar::Util::weaken('.$self.'->{'.$key.'}) if ref('.$self.'->{'.$key.'});' . "\n"; + } + + if ($trigger) { + $accessor .= '$trigger->('.$self.', '.$value.');' . "\n"; + } + + $accessor .= "}\n"; + } + else { + $accessor .= 'Carp::confess("Cannot assign a value to a read-only accessor") if scalar(@_) >= 2;' . "\n"; + } + + if ($attribute->is_lazy) { + $accessor .= $self.'->{'.$key.'} = '; + + $accessor .= $attribute->has_builder + ? $self.'->$builder' + : ref($default) eq 'CODE' + ? '$default->('.$self.')' + : '$default'; + $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n"; + } + + if ($should_deref) { + if (ref($constraint) && $constraint->name =~ '^ArrayRef\b') { + $accessor .= 'if (wantarray) { + return @{ '.$self.'->{'.$key.'} || [] }; + }'; + } + else { + $accessor .= 'if (wantarray) { + return %{ '.$self.'->{'.$key.'} || {} }; + }'; + } + } + + $accessor .= 'return '.$self.'->{'.$key.'}; + }'; + + my $sub = eval $accessor; + Carp::confess($@) if $@; + return $sub; +} + + +sub _generate_predicate { my $attribute = shift; - my $key = $attribute->inlined_name; + my $key = $attribute->_inlined_name; my $predicate = 'sub { exists($_[0]->{'.$key.'}) }'; @@ -74,9 +175,9 @@ sub generate_predicate { return $sub; } -sub generate_clearer { +sub _generate_clearer { my $attribute = shift; - my $key = $attribute->inlined_name; + my $key = $attribute->_inlined_name; my $clearer = 'sub { delete($_[0]->{'.$key.'}) }'; @@ -85,7 +186,7 @@ sub generate_clearer { return $sub; } -sub generate_handles { +sub _generate_handles { my $attribute = shift; my $reader = $attribute->name; my %handles = $attribute->_canonicalize_handles($attribute->handles); @@ -120,13 +221,6 @@ sub create { if exists $args{coerce}; if (exists $args{isa}) { - confess "Got isa => $args{isa}, but Mouse does not yet support parameterized types for containers other than ArrayRef and HashRef (rt.cpan.org #39795)" - if $args{isa} =~ /^([^\[]+)\[.+\]$/ && - $1 ne 'ArrayRef' && - $1 ne 'HashRef' && - $1 ne 'Maybe' - ; - my $type_constraint = delete $args{isa}; $args{type_constraint}= Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($type_constraint); } @@ -137,30 +231,40 @@ sub create { $class->add_attribute($attribute); + my $associated_methods = 0; + + my $is_metadata = $attribute->_is_metadata || ''; + # install an accessor - if ($attribute->_is_metadata eq 'rw' || $attribute->_is_metadata eq 'ro') { - my $code = Mouse::Meta::Method::Accessor->generate_accessor_method_inline( - $attribute, - ); + if ($is_metadata eq 'rw' || $is_metadata eq 'ro') { + my $code = $attribute->_generate_accessor(); $class->add_method($name => $code); + $associated_methods++; } for my $method (qw/predicate clearer/) { my $predicate = "has_$method"; if ($attribute->$predicate) { - my $generator = "generate_$method"; + my $generator = "_generate_$method"; my $coderef = $attribute->$generator; $class->add_method($attribute->$method => $coderef); + $associated_methods++; } } if ($attribute->has_handles) { - my $method_map = $attribute->generate_handles; + my $method_map = $attribute->_generate_handles; for my $method_name (keys %$method_map) { $class->add_method($method_name => $method_map->{$method_name}); + $associated_methods++; } } + if($associated_methods == 0 && $is_metadata ne 'bare'){ + Carp::cluck(qq{Attribute ($name) of class }.$class->name.qq{ has no associated methods (did you mean to provide an "is" argument?)}); + + } + return $attribute; } @@ -348,22 +452,6 @@ installed. Some error checking is done. Informational methods. -=head2 generate_accessor -> CODE - -Creates a new code reference for the attribute's accessor. - -=head2 generate_predicate -> CODE - -Creates a new code reference for the attribute's predicate. - -=head2 generate_clearer -> CODE - -Creates a new code reference for the attribute's clearer. - -=head2 generate_handles -> { MethodName => CODE } - -Creates a new code reference for each of the attribute's handles methods. - =head2 verify_against_type_constraint Item -> 1 | ERROR Checks that the given value passes this attribute's type constraint. Returns 1 diff --git a/lib/Mouse/Meta/Class.pm b/lib/Mouse/Meta/Class.pm index 4761e5d..7ba5692 100644 --- a/lib/Mouse/Meta/Class.pm +++ b/lib/Mouse/Meta/Class.pm @@ -20,12 +20,13 @@ do { } sub initialize { - my $class = blessed($_[0]) || $_[0]; - my $name = $_[1]; + my($class, $package_name, @args) = @_; - $METACLASS_CACHE{$name} = $class->new(name => $name) - if !exists($METACLASS_CACHE{$name}); - return $METACLASS_CACHE{$name}; + ($package_name && !ref($package_name)) + || confess("You must pass a package name and it cannot be blessed"); + + return $METACLASS_CACHE{$package_name} + ||= $class->_construct_class_instance(package => $package_name, @args); } # Means of accessing all the metaclasses that have @@ -40,21 +41,20 @@ do { sub remove_metaclass_by_name { $METACLASS_CACHE{$_[0]} = undef } }; -sub new { - my $class = shift; - my %args = @_; +sub _construct_class_instance { + my($class, %args) = @_; - $args{attributes} = {}; + $args{attributes} = {}; $args{superclasses} = do { no strict 'refs'; - \@{ $args{name} . '::ISA' }; + \@{ $args{package} . '::ISA' }; }; $args{roles} ||= []; bless \%args, $class; } -sub name { $_[0]->{name} } +sub name { $_[0]->{package} } sub superclasses { my $self = shift; @@ -319,7 +319,7 @@ sub does_role { } sub create { - my ($self, $package_name, %options) = @_; + my ($class, $package_name, %options) = @_; (ref $options{superclasses} eq 'ARRAY') || confess "You must pass an ARRAY ref of superclasses" @@ -356,11 +356,11 @@ sub create { version authority )}; - my $meta = $self->initialize( $package_name => %initialize_options ); + my $meta = $class->initialize( $package_name => %initialize_options ); # FIXME totally lame $meta->add_method('meta' => sub { - $self->initialize(ref($_[0]) || $_[0]); + Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]); }); $meta->superclasses(@{$options{superclasses}}) diff --git a/lib/Mouse/Meta/Method/Accessor.pm b/lib/Mouse/Meta/Method/Accessor.pm deleted file mode 100644 index 38531bc..0000000 --- a/lib/Mouse/Meta/Method/Accessor.pm +++ /dev/null @@ -1,111 +0,0 @@ -package Mouse::Meta::Method::Accessor; -use strict; -use warnings; -use Carp (); - -# internal use only. do not call directly -sub generate_accessor_method_inline { - my ($class, $attribute) = @_; - - my $name = $attribute->name; - my $default = $attribute->default; - my $constraint = $attribute->type_constraint; - my $builder = $attribute->builder; - my $trigger = $attribute->trigger; - my $is_weak = $attribute->is_weak_ref; - my $should_deref = $attribute->should_auto_deref; - my $should_coerce = $attribute->should_coerce; - - my $compiled_type_constraint = $constraint ? $constraint->{_compiled_type_constraint} : undef; - - my $self = '$_[0]'; - my $key = $attribute->inlined_name; - - my $accessor = - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - "sub {\n"; - if ($attribute->_is_metadata eq 'rw') { - $accessor .= - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'if (scalar(@_) >= 2) {' . "\n"; - - my $value = '$_[1]'; - - if ($constraint) { - if ($should_coerce) { - $accessor .= - "\n". - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'my $val = Mouse::Util::TypeConstraints->typecast_constraints("'.$attribute->associated_class->name.'", $attribute->{type_constraint}, '.$value.');'; - $value = '$val'; - } - if ($compiled_type_constraint) { - $accessor .= - "\n". - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'unless ($compiled_type_constraint->('.$value.')) { - $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint}); - }' . "\n"; - } else { - $accessor .= - "\n". - '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" . - 'unless ($constraint->check('.$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 - # this setter - $accessor .= 'return ' if !$is_weak && !$trigger && !$should_deref; - - $accessor .= $self.'->{'.$key.'} = '.$value.';' . "\n"; - - if ($is_weak) { - $accessor .= 'Scalar::Util::weaken('.$self.'->{'.$key.'}) if ref('.$self.'->{'.$key.'});' . "\n"; - } - - if ($trigger) { - $accessor .= '$trigger->('.$self.', '.$value.');' . "\n"; - } - - $accessor .= "}\n"; - } - else { - $accessor .= 'Carp::confess("Cannot assign a value to a read-only accessor") if scalar(@_) >= 2;' . "\n"; - } - - if ($attribute->is_lazy) { - $accessor .= $self.'->{'.$key.'} = '; - - $accessor .= $attribute->has_builder - ? $self.'->$builder' - : ref($default) eq 'CODE' - ? '$default->('.$self.')' - : '$default'; - $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n"; - } - - if ($should_deref) { - if (ref($constraint) && $constraint->name =~ '^ArrayRef\b') { - $accessor .= 'if (wantarray) { - return @{ '.$self.'->{'.$key.'} || [] }; - }'; - } - else { - $accessor .= 'if (wantarray) { - return %{ '.$self.'->{'.$key.'} || {} }; - }'; - } - } - - $accessor .= 'return '.$self.'->{'.$key.'}; - }'; - - my $sub = eval $accessor; - Carp::confess($@) if $@; - return $sub; -} - -1; diff --git a/lib/Mouse/Meta/Role.pm b/lib/Mouse/Meta/Role.pm index 13daeaf..4910f72 100644 --- a/lib/Mouse/Meta/Role.pm +++ b/lib/Mouse/Meta/Role.pm @@ -2,6 +2,7 @@ package Mouse::Meta::Role; use strict; use warnings; use Carp 'confess'; + use Mouse::Util qw(version authority identifier); do { diff --git a/lib/Mouse/Meta/TypeConstraint.pm b/lib/Mouse/Meta/TypeConstraint.pm index 538e3b2..51b0867 100644 --- a/lib/Mouse/Meta/TypeConstraint.pm +++ b/lib/Mouse/Meta/TypeConstraint.pm @@ -1,6 +1,8 @@ package Mouse::Meta::TypeConstraint; use strict; use warnings; +use Carp (); + use overload '""' => sub { shift->{name} }, # stringify to tc name fallback => 1; @@ -28,6 +30,26 @@ sub check { $self->{_compiled_type_constraint}->(@_); } +sub validate { + my ($self, $value) = @_; + if ($self->{_compiled_type_constraint}->($value)) { + return undef; + } + else { + $self->get_message($value); + } +} + +sub assert_valid { + my ($self, $value) = @_; + + my $error = $self->validate($value); + return 1 if ! defined $error; + + Carp::confess($error); +} + + sub message { return $_[0]->{message}; } diff --git a/lib/Mouse/Object.pm b/lib/Mouse/Object.pm index ddcf41f..745d1f2 100644 --- a/lib/Mouse/Object.pm +++ b/lib/Mouse/Object.pm @@ -136,7 +136,7 @@ sub does { || confess "You must supply a role name to does()"; my $meta = $self->meta; foreach my $class ($meta->linearized_isa) { - my $m = $meta->initialize($class); + my $m = ref($meta)->initialize($class); return 1 if $m->can('does_role') && $m->does_role($role_name); } diff --git a/lib/Mouse/Util/TypeConstraints.pm b/lib/Mouse/Util/TypeConstraints.pm index 4f77130..993e8e0 100644 --- a/lib/Mouse/Util/TypeConstraints.pm +++ b/lib/Mouse/Util/TypeConstraints.pm @@ -308,6 +308,13 @@ sub find_type_constraint { sub find_or_create_isa_type_constraint { my $type_constraint = shift; + Carp::confess("Got isa => type_constraints, but Mouse does not yet support parameterized types for containers other than ArrayRef and HashRef and Maybe (rt.cpan.org #39795)") + if $type_constraint =~ /\A ( [^\[]+ ) \[\.+\] \z/xms && + $1 ne 'ArrayRef' && + $1 ne 'HashRef' && + $1 ne 'Maybe' + ; + my $code; $type_constraint =~ s/\s+//g; diff --git a/t/000-recipes/001_point.t b/t/000-recipes/001_point.t index 1f52f0f..90b989b 100644 --- a/t/000-recipes/001_point.t +++ b/t/000-recipes/001_point.t @@ -36,7 +36,7 @@ use Test::Exception; extends 'Point'; - has 'z' => (isa => 'Int'); + has 'z' => (isa => 'Int', is => 'bare'); after 'clear' => sub { my $self = shift; diff --git a/t/007-attributes.t b/t/007-attributes.t index 4316e25..fdb3ed3 100644 --- a/t/007-attributes.t +++ b/t/007-attributes.t @@ -8,7 +8,9 @@ do { package Class; use Mouse; - has 'x'; + has 'x' => ( + is => 'bare', + ); has 'y' => ( is => 'ro', diff --git a/t/010-required.t b/t/010-required.t index 161717c..e6a6990 100644 --- a/t/010-required.t +++ b/t/010-required.t @@ -9,15 +9,18 @@ do { use Mouse; has foo => ( + is => 'bare', required => 1, ); has bar => ( + is => 'bare', required => 1, default => 50, ); has baz => ( + is => 'bare', required => 1, default => sub { 10 }, ); diff --git a/t/025-more-isa.t b/t/025-more-isa.t index 576d5e1..022c89c 100755 --- a/t/025-more-isa.t +++ b/t/025-more-isa.t @@ -54,6 +54,7 @@ do { use Mouse; has oops => ( + is => 'bare', isa => 'Int', default => "yikes", ); diff --git a/t/029-new.t b/t/029-new.t index 4e642eb..fe660a1 100644 --- a/t/029-new.t +++ b/t/029-new.t @@ -8,7 +8,9 @@ do { package Class; use Mouse; - has 'x'; + has x => ( + is => 'bare', + ); has y => ( is => 'ro', diff --git a/t/030_roles/002_role.t b/t/030_roles/002_role.t index 2dfe39b..448d492 100755 --- a/t/030_roles/002_role.t +++ b/t/030_roles/002_role.t @@ -85,16 +85,13 @@ is_deeply( ok($foo_role->has_attribute('bar'), '... FooRole does have the bar attribute'); -is_deeply( - $foo_role->get_attribute('bar'), - { is => 'rw', isa => 'Foo' }, - '... got the correct description of the bar attribute'); +is $foo_role->get_attribute('bar')->{is}, 'rw', '... got the correct description of the bar attribute'; ok($foo_role->has_attribute('baz'), '... FooRole does have the baz attribute'); -is_deeply( - $foo_role->get_attribute('baz'), - { is => 'ro' }, +is( + $foo_role->get_attribute('baz')->{is}, + 'ro', '... got the correct description of the baz attribute'); # method modifiers diff --git a/t/400-define-role.t b/t/400-define-role.t index 7202797..1441463 100644 --- a/t/400-define-role.t +++ b/t/400-define-role.t @@ -44,7 +44,7 @@ lives_ok { package Role; use Mouse::Role; - has 'foo'; + has 'foo' => (is => 'bare'); no Mouse::Role; }; diff --git a/t/402-attribute-application.t b/t/402-attribute-application.t index fbb400d..e4745c5 100644 --- a/t/402-attribute-application.t +++ b/t/402-attribute-application.t @@ -9,13 +9,14 @@ do { use Mouse::Role; has 'attr' => ( + is => 'bare', default => 'Role', ); no Mouse::Role; }; -is_deeply(Role->meta->get_attribute('attr'), {default => 'Role'}); +is(Role->meta->get_attribute('attr')->{default}, 'Role'); do { package Class; @@ -33,6 +34,7 @@ do { use Mouse::Role; has 'attr' => ( + is => 'bare', default => 'Role2', ); @@ -55,6 +57,7 @@ lives_ok { with 'Role'; has attr => ( + is => 'bare', default => 'Class3', ); }; @@ -66,6 +69,7 @@ lives_ok { use Mouse; has attr => ( + is => 'bare', default => 'Class::Parent', ); };