From: Dave Rolsky Date: Wed, 6 Aug 2008 16:43:13 +0000 (+0000) Subject: Step 1: Moose::Exporter lets Moose & Moose::Role have the same X-Git-Tag: 0_55_01~43^2~35 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5bd4db9b7e1ab30af23aa908be646f5b52010fdf;p=gitmo%2FMoose.git Step 1: Moose::Exporter lets Moose & Moose::Role have the same functionality as they did before. Next up, making Moose::Exporter work as a _re-exporter_ so MooseX::Foo can export all of Moose and its own functions too. --- diff --git a/lib/Moose.pm b/lib/Moose.pm index a421c74..7aff9ec 100644 --- a/lib/Moose.pm +++ b/lib/Moose.pm @@ -10,7 +10,7 @@ our $AUTHORITY = 'cpan:STEVAN'; use Scalar::Util 'blessed'; use Carp 'confess', 'croak', 'cluck'; -use Sub::Exporter; +use Moose::Exporter; use Class::MOP 0.64; @@ -26,207 +26,150 @@ use Moose::Object; use Moose::Util::TypeConstraints; use Moose::Util (); -{ - my $CALLER; - - my %exports = ( - extends => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::extends' => sub (@) { - croak "Must derive at least one class" unless @_; - - my @supers = @_; - foreach my $super (@supers) { - Class::MOP::load_class($super); - croak "You cannot inherit from a Moose Role ($super)" - if $super->can('meta') && - blessed $super->meta && - $super->meta->isa('Moose::Meta::Role') - } - - - - # this checks the metaclass to make sure - # it is correct, sometimes it can get out - # of sync when the classes are being built - my $meta = $class->meta->_fix_metaclass_incompatability(@supers); - $meta->superclasses(@supers); - }); - }, - with => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::with' => sub (@) { - Moose::Util::apply_all_roles($class->meta, @_) - }); - }, - has => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::has' => sub ($;%) { - my $name = shift; - croak 'Usage: has \'name\' => ( key => value, ... )' if @_ == 1; - my %options = @_; - my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; - $class->meta->add_attribute( $_, %options ) for @$attrs; - }); - }, - before => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::before' => sub (@&) { - Moose::Util::add_method_modifier($class, 'before', \@_); - }); - }, - after => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::after' => sub (@&) { - Moose::Util::add_method_modifier($class, 'after', \@_); - }); - }, - around => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::around' => sub (@&) { - Moose::Util::add_method_modifier($class, 'around', \@_); - }); - }, - super => sub { - return Class::MOP::subname('Moose::super' => sub { - return unless our $SUPER_BODY; $SUPER_BODY->(our @SUPER_ARGS) - }); - }, - override => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::override' => sub ($&) { - my ( $name, $method ) = @_; - $class->meta->add_override_method_modifier( $name => $method ); - }); - }, - inner => sub { - return Class::MOP::subname('Moose::inner' => sub { - my $pkg = caller(); - our ( %INNER_BODY, %INNER_ARGS ); - - if ( my $body = $INNER_BODY{$pkg} ) { - my @args = @{ $INNER_ARGS{$pkg} }; - local $INNER_ARGS{$pkg}; - local $INNER_BODY{$pkg}; - return $body->(@args); - } else { - return; - } - }); - }, - augment => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::augment' => sub (@&) { - my ( $name, $method ) = @_; - $class->meta->add_augment_method_modifier( $name => $method ); - }); - }, - make_immutable => sub { - my $class = $CALLER; - return Class::MOP::subname('Moose::make_immutable' => sub { - cluck "The make_immutable keyword has been deprecated, " . - "please go back to __PACKAGE__->meta->make_immutable\n"; - $class->meta->make_immutable(@_); - }); - }, - confess => sub { - return \&Carp::confess; - }, - blessed => sub { - return \&Scalar::Util::blessed; - }, - ); +sub extends { + my $class = shift; - my $exporter = Sub::Exporter::build_exporter( - { - exports => \%exports, - groups => { default => [':all'] } - } - ); + croak "Must derive at least one class" unless @_; - # 1 extra level because it's called by import so there's a layer of indirection - sub _get_caller{ - my $offset = 1; - return - (ref $_[1] && defined $_[1]->{into}) - ? $_[1]->{into} - : (ref $_[1] && defined $_[1]->{into_level}) - ? caller($offset + $_[1]->{into_level}) - : caller($offset); + my @supers = @_; + foreach my $super (@supers) { + Class::MOP::load_class($super); + croak "You cannot inherit from a Moose Role ($super)" + if $super->can('meta') && + blessed $super->meta && + $super->meta->isa('Moose::Meta::Role') } - sub import { - $CALLER = _get_caller(@_); - # this works because both pragmas set $^H (see perldoc perlvar) - # which affects the current compilation - i.e. the file who use'd - # us - which is why we don't need to do anything special to make - # it affect that file rather than this one (which is already compiled) - strict->import; - warnings->import; + # this checks the metaclass to make sure + # it is correct, sometimes it can get out + # of sync when the classes are being built + my $meta = $class->meta->_fix_metaclass_incompatability(@supers); + $meta->superclasses(@supers); +} - # we should never export to main - if ($CALLER eq 'main') { - warn qq{Moose does not export its sugar to the 'main' package.\n}; - return; - } +sub with { + my $class = shift; + Moose::Util::apply_all_roles($class->meta, @_); +} - init_meta( $CALLER, 'Moose::Object' ); +sub has { + my $class = shift; + my $name = shift; + croak 'Usage: has \'name\' => ( key => value, ... )' if @_ == 1; + my %options = @_; + my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; + $class->meta->add_attribute( $_, %options ) for @$attrs; +} - goto $exporter; - } - - # NOTE: - # This is for special use by - # some modules and stuff, I - # dont know if it is sane enough - # to document actually. - # - SL - sub __CURRY_EXPORTS_FOR_CLASS__ { - $CALLER = shift; - ($CALLER ne 'Moose') - || croak "_import_into must be called a function, not a method"; - ($CALLER->can('meta') && $CALLER->meta->isa('Class::MOP::Class')) - || croak "Cannot call _import_into on a package ($CALLER) without a metaclass"; - return map { $_ => $exports{$_}->() } (@_ ? @_ : keys %exports); - } +sub before { + my $class = shift; + Moose::Util::add_method_modifier($class, 'before', \@_); +} - sub unimport { - my $class = _get_caller(@_); +sub after { + my $class = shift; + Moose::Util::add_method_modifier($class, 'after', \@_); +} - _remove_keywords( - source => __PACKAGE__, - package => $class, - keywords => [ keys %exports ], - ); +sub around { + my $class = shift; + Moose::Util::add_method_modifier($class, 'around', \@_); +} + +sub super { + return unless our $SUPER_BODY; $SUPER_BODY->(our @SUPER_ARGS); +} + +sub override { + my $class = shift; + my ( $name, $method ) = @_; + $class->meta->add_override_method_modifier( $name => $method ); +} + +sub inner { + my $pkg = caller(); + our ( %INNER_BODY, %INNER_ARGS ); + + if ( my $body = $INNER_BODY{$pkg} ) { + my @args = @{ $INNER_ARGS{$pkg} }; + local $INNER_ARGS{$pkg}; + local $INNER_BODY{$pkg}; + return $body->(@args); + } else { + return; } +} +sub augment { + my $class = shift; + my ( $name, $method ) = @_; + $class->meta->add_augment_method_modifier( $name => $method ); } -sub _remove_keywords { - my ( %args ) = @_; +sub make_immutable { + my $class = shift; + cluck "The make_immutable keyword has been deprecated, " . + "please go back to __PACKAGE__->meta->make_immutable\n"; + $class->meta->make_immutable(@_); +} - my $source = $args{source}; - my $package = $args{package}; +my $exporter = Moose::Exporter->build_exporter( + with_caller => [ + qw( extends with has before after around override augment make_immutable ) + ], + as_is => [ + qw( super inner ), + \&Carp::confess, + \&Scalar::Util::blessed, + ], +); + +sub import { + my $caller = Moose::Exporter->get_caller(@_); + + # this works because both pragmas set $^H (see perldoc perlvar) + # which affects the current compilation - i.e. the file who use'd + # us - which is why we don't need to do anything special to make + # it affect that file rather than this one (which is already compiled) + + strict->import; + warnings->import; + + # we should never export to main + if ($caller eq 'main') { + warn qq{Moose does not export its sugar to the 'main' package.\n}; + return; + } - no strict 'refs'; + init_meta($caller, 'Moose::Object'); - # loop through the keywords ... - foreach my $name ( @{ $args{keywords} } ) { + goto $exporter; +} - # if we find one ... - if ( defined &{ $package . '::' . $name } ) { - my $keyword = \&{ $package . '::' . $name }; +# NOTE: +# This is for special use by +# some modules and stuff, I +# dont know if it is sane enough +# to document actually. +# - SL +sub __CURRY_EXPORTS_FOR_CLASS__ { + my $caller = shift; + ($caller ne 'Moose') + || croak "_import_into must be called a function, not a method"; + ($caller->can('meta') && $caller->meta->isa('Class::MOP::Class')) + || croak "Cannot call _import_into on a package ($caller) without a metaclass"; +# return map { $_ => $exports{$_}->() } (@_ ? @_ : keys %exports); +} - # make sure it is from us - my ($pkg_name) = Class::MOP::get_code_info($keyword); - next if $pkg_name ne $source; +sub unimport { + my $caller = Moose::Exporter->get_caller(@_); - # and if it is from us, then undef the slot - delete ${ $package . '::' }{$name}; - } - } + Moose::Exporter->remove_keywords( + source => __PACKAGE__, + from => $caller, + ); } sub init_meta { diff --git a/lib/Moose/Exporter.pm b/lib/Moose/Exporter.pm new file mode 100644 index 0000000..4862563 --- /dev/null +++ b/lib/Moose/Exporter.pm @@ -0,0 +1,85 @@ +package Moose::Exporter; + +use strict; +use warnings; + +use Class::MOP; +use Sub::Exporter; + + +sub get_caller{ + # 1 extra level because it's called by import so there's a layer of indirection + my $offset = 1; + + return + (ref $_[1] && defined $_[1]->{into}) + ? $_[1]->{into} + : (ref $_[1] && defined $_[1]->{into_level}) + ? caller($offset + $_[1]->{into_level}) + : caller($offset); +} + +my %EXPORTED; +sub build_exporter { + my $class = shift; + my %args = @_; + + my $exporting_pkg = caller(); + + my %exports; + for my $name ( @{ $args{with_caller} } ) { + my $sub = do { no strict 'refs'; \&{ $exporting_pkg . '::' . $name } }; + + my $wrapped = Class::MOP::subname( + $exporting_pkg . '::' . $name => sub { $sub->( scalar caller(), @_ ) } ); + + $exports{$name} = sub { $wrapped }; + + push @{ $EXPORTED{$exporting_pkg} }, $name; + } + + for my $name ( @{ $args{as_is} } ) { + my $sub; + + if ( ref $name ) { + $sub = $name; + $name = ( Class::MOP::get_code_info($name) )[1]; + } + else { + $sub = do { no strict 'refs'; \&{ $exporting_pkg . '::' . $name } }; + + push @{ $EXPORTED{$exporting_pkg} }, $name; + } + + $exports{$name} = sub { $sub }; + } + + return Sub::Exporter::build_exporter( + { + exports => \%exports, + groups => { default => [':all'] } + } + ); +} + +sub remove_keywords { + my $class = shift; + my %args = @_; + + no strict 'refs'; + + for my $name ( @{ $EXPORTED{ $args{source} } } ) { + if ( defined &{ $args{from} . '::' . $name } ) { + my $keyword = \&{ $args{from} . '::' . $name }; + + # make sure it is from us + my ($pkg_name) = Class::MOP::get_code_info($keyword); + next if $pkg_name ne $args{source}; + + # and if it is from us, then undef the slot + delete ${ $args{from} . '::' }{$name}; + } + } +} + +1; diff --git a/lib/Moose/Role.pm b/lib/Moose/Role.pm index de4b698..7a29948 100644 --- a/lib/Moose/Role.pm +++ b/lib/Moose/Role.pm @@ -20,10 +20,10 @@ use Moose::Meta::Role; use Moose::Util::TypeConstraints; { - my ( $CALLER, %METAS ); + my %METAS; - sub _find_meta { - my $role = $CALLER; + sub init_meta { + my $role = shift; return $METAS{$role} if exists $METAS{$role}; @@ -43,165 +43,135 @@ use Moose::Util::TypeConstraints; return $METAS{$role} = $meta; } +} +sub extends { + croak "Roles do not currently support 'extends'"; +} - my %exports = ( - extends => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::extends' => sub { - croak "Roles do not currently support 'extends'" - }); - }, - with => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::with' => sub (@) { - Moose::Util::apply_all_roles($meta, @_) - }); - }, - requires => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::requires' => sub (@) { - croak "Must specify at least one method" unless @_; - $meta->add_required_methods(@_); - }); - }, - excludes => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::excludes' => sub (@) { - croak "Must specify at least one role" unless @_; - $meta->add_excluded_roles(@_); - }); - }, - has => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::has' => sub ($;%) { - my $name = shift; - croak 'Usage: has \'name\' => ( key => value, ... )' if @_ == 1; - my %options = @_; - my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; - $meta->add_attribute( $_, %options ) for @$attrs; - }); - }, - before => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::before' => sub (@&) { - my $code = pop @_; - do { - croak "Moose::Role do not currently support " - . ref($_) - . " references for before method modifiers" - if ref $_; - $meta->add_before_method_modifier($_, $code) - } for @_; - }); - }, - after => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::after' => sub (@&) { - my $code = pop @_; - do { - croak "Moose::Role do not currently support " - . ref($_) - . " references for after method modifiers" - if ref $_; - $meta->add_after_method_modifier($_, $code) - } for @_; - }); - }, - around => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::around' => sub (@&) { - my $code = pop @_; - do { - croak "Moose::Role do not currently support " - . ref($_) - . " references for around method modifiers" - if ref $_; - $meta->add_around_method_modifier($_, $code) - } for @_; - }); - }, - # see Moose.pm for discussion - super => sub { - return Class::MOP::subname('Moose::Role::super' => sub { - return unless $Moose::SUPER_BODY; $Moose::SUPER_BODY->(@Moose::SUPER_ARGS) - }); - }, - override => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::override' => sub ($&) { - my ($name, $code) = @_; - $meta->add_override_method_modifier($name, $code); - }); - }, - inner => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::inner' => sub { - croak "Moose::Role cannot support 'inner'"; - }); - }, - augment => sub { - my $meta = _find_meta(); - return Class::MOP::subname('Moose::Role::augment' => sub { - croak "Moose::Role cannot support 'augment'"; - }); - }, - confess => sub { - return \&Carp::confess; - }, - blessed => sub { - return \&Scalar::Util::blessed; - } - ); +sub with { + Moose::Util::apply_all_roles( shift->meta(), @_ ); +} - my $exporter = Sub::Exporter::build_exporter({ - exports => \%exports, - groups => { - default => [':all'] - } - }); +sub requires { + my $meta = shift->meta(); + croak "Must specify at least one method" unless @_; + $meta->add_required_methods(@_); +} - sub import { - $CALLER = - ref $_[1] && defined $_[1]->{into} ? $_[1]->{into} - : ref $_[1] - && defined $_[1]->{into_level} ? caller( $_[1]->{into_level} ) - : caller(); +sub excludes { + my $meta = shift->meta(); + croak "Must specify at least one role" unless @_; + $meta->add_excluded_roles(@_); +} - # this works because both pragmas set $^H (see perldoc perlvar) - # which affects the current compilation - i.e. the file who use'd - # us - which is why we don't need to do anything special to make - # it affect that file rather than this one (which is already compiled) +sub has { + my $meta = shift->meta(); + my $name = shift; + croak 'Usage: has \'name\' => ( key => value, ... )' if @_ == 1; + my %options = @_; + my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; + $meta->add_attribute( $_, %options ) for @$attrs; +} - strict->import; - warnings->import; +sub before { + my $meta = shift->meta(); + my $code = pop @_; - # we should never export to main - return if $CALLER eq 'main'; + for (@_) { + croak "Moose::Role do not currently support " + . ref($_) + . " references for before method modifiers" + if ref $_; + $meta->add_before_method_modifier( $_, $code ); + } +} + +sub after { + my $meta = shift->meta(); + + my $code = pop @_; + for (@_) { + croak "Moose::Role do not currently support " + . ref($_) + . " references for after method modifiers" + if ref $_; + $meta->add_after_method_modifier( $_, $code ); + } +} - goto $exporter; - }; +sub around { + my $meta = shift->meta(); + my $code = pop @_; + for (@_) { + croak "Moose::Role do not currently support " + . ref($_) + . " references for around method modifiers" + if ref $_; + $meta->add_around_method_modifier( $_, $code ); + } +} - sub unimport { - no strict 'refs'; - my $class = Moose::_get_caller(@_); +# see Moose.pm for discussion +sub super { + return unless $Moose::SUPER_BODY; + $Moose::SUPER_BODY->(@Moose::SUPER_ARGS); +} - # loop through the exports ... - foreach my $name ( keys %exports ) { +sub override { + my $meta = shift->meta(); + my ( $name, $code ) = @_; + $meta->add_override_method_modifier( $name, $code ); +} - # if we find one ... - if ( defined &{ $class . '::' . $name } ) { - my $keyword = \&{ $class . '::' . $name }; +sub inner { + croak "Moose::Role cannot support 'inner'"; +} - # make sure it is from Moose::Role - my ($pkg_name) = Class::MOP::get_code_info($keyword); - next if $pkg_name ne 'Moose::Role'; +sub augment { + croak "Moose::Role cannot support 'augment'"; +} - # and if it is from Moose::Role then undef the slot - delete ${ $class . '::' }{$name}; - } - } +my $exporter = Moose::Exporter->build_exporter( + with_caller => [ + qw( with requires excludes has before after around override make_immutable ) + ], + as_is => [ + qw( extends super inner augment ), + \&Carp::confess, + \&Scalar::Util::blessed, + ], +); + +sub import { + my $caller = Moose::Exporter->get_caller(@_); + + # this works because both pragmas set $^H (see perldoc perlvar) + # which affects the current compilation - i.e. the file who use'd + # us - which is why we don't need to do anything special to make + # it affect that file rather than this one (which is already compiled) + + strict->import; + warnings->import; + + # we should never export to main + if ($caller eq 'main') { + warn qq{Moose::Role does not export its sugar to the 'main' package.\n}; + return; } + + init_meta($caller); + + goto $exporter; +} + +sub unimport { + my $caller = Moose::Exporter->get_caller(@_); + + Moose::Exporter->remove_keywords( + source => __PACKAGE__, + from => $caller, + ); } 1;