Merge branch 'master' into topic/strict_export_list
Dave Rolsky [Mon, 7 Sep 2009 16:58:50 +0000 (11:58 -0500)]
Conflicts:
t/050_metaclasses/012_moose_exporter.t

1  2 
Changes
lib/Moose/Exporter.pm
t/010_basics/022_moose_exporter_groups.t
t/050_metaclasses/012_moose_exporter.t

diff --combined Changes
+++ b/Changes
@@@ -5,12 -5,21 +5,21 @@@ for, noteworthy changes
      * Moose::Meta::Attribute::Native
        - Fix Hash, which still had 'empty' instead of 'is_empty'. (hdp)
  
+     * Moose::Meta::Attribute::Native::Trait::Array
+       - Added a number of functions from List::Util and List::MoreUtils,
+         including reduce, shuffle, uniq, and natatime. (doy)
      * Moose::Exporter
        - This module will now generate an init_meta method for your exporting
          class if you pass it options for
          Moose::Util::MetaRole::apply_metaclass_roles or
          apply_base_class_roles. This eliminates a lot of repetitive
          boilerplate for typical MooseX modules. (doy).
+       - Documented the with_meta feature, which is a replacement for
+         with_caller. This feature was added by josh a while ago.
+       - The with_caller feature is now deprecated, but will not issue a
+         warning yet. (Dave Rolsky)
  
  0.89_01 Wed Sep 2, 2009
      * Moose::Meta::Attribute
      * Moose
        - Correct POD for builder to point to Recipe8, not 9. (gphat)
  
 +    * Moose::Exporter
 +      - When a nonexistent sub name is passed to as_is, with_caller, or
 +        with_meta, throw a warning and skip the exporting, rather than
 +        installing a broken sub. (doy)
 +
  0.89 Thu Aug 13, 2009
      * Moose::Manual::Attributes
        - Clarify "is", include discussion of "bare". (Sartak)
diff --combined lib/Moose/Exporter.pm
@@@ -37,14 -37,14 +37,14 @@@ sub build_import_methods 
  
      my $export_recorder = {};
  
-     my ( $exports, $is_removable, $groups )
+     my ( $exports, $is_removable )
          = $class->_make_sub_exporter_params(
          [ @exports_from, $exporting_package ], $export_recorder );
  
      my $exporter = Sub::Exporter::build_exporter(
          {
              exports => $exports,
-             groups  => { default => [':all'], %$groups }
+             groups  => { default => [':all'] }
          }
      );
  
@@@ -119,7 -119,6 +119,6 @@@ sub _make_sub_exporter_params 
      my $packages          = shift;
      my $export_recorder   = shift;
  
-     my %groups;
      my %exports;
      my %is_removable;
  
          my $args = $EXPORT_SPEC{$package}
              or die "The $package package does not use Moose::Exporter\n";
  
-         # one group for each 'also' package
-         $groups{$package} = [
-             @{ $args->{with_caller} || [] },
-             @{ $args->{with_meta}   || [] },
-             @{ $args->{as_is}       || [] },
-             map ":$_",
-             keys %{ $args->{groups} || {} }
-         ];
-         for my $name ( @{ $args->{with_caller} } ) {
+         for my $name ( @{ $args->{with_meta} } ) {
              my $sub = do {
                  no strict 'refs';
                  \&{ $package . '::' . $name };
              };
  
 +            if ( !defined(&$sub) ) {
 +                Carp::cluck
 +                    "Trying to export undefined sub ${package}::${name}";
 +                next;
 +            }
 +
              my $fq_name = $package . '::' . $name;
  
-             $exports{$name} = $class->_make_wrapped_sub(
+             $exports{$name} = $class->_make_wrapped_sub_with_meta(
                  $fq_name,
                  $sub,
                  $export_recorder,
              $is_removable{$name} = 1;
          }
  
-         for my $name ( @{ $args->{with_meta} } ) {
+         for my $name ( @{ $args->{with_caller} } ) {
              my $sub = do {
                  no strict 'refs';
                  \&{ $package . '::' . $name };
              };
  
 +            if ( !defined(&$sub) ) {
 +                Carp::cluck
 +                    "Trying to export undefined sub ${package}::${name}";
 +                next;
 +            }
 +
              my $fq_name = $package . '::' . $name;
  
-             $exports{$name} = $class->_make_wrapped_sub_with_meta(
+             $exports{$name} = $class->_make_wrapped_sub(
                  $fq_name,
                  $sub,
                  $export_recorder,
          }
  
          for my $name ( @{ $args->{as_is} } ) {
 -            my $sub;
 +            my ($sub, $coderef_name);
  
              if ( ref $name ) {
                  $sub  = $name;
                  # really want to keep these subs or not, we err on the
                  # safe side and leave them in.
                  my $coderef_pkg;
 -                ( $coderef_pkg, $name ) = Class::MOP::get_code_info($name);
 +                ( $coderef_pkg, $coderef_name )
 +                    = Class::MOP::get_code_info($name);
  
 -                $is_removable{$name} = $coderef_pkg eq $package ? 1 : 0;
 +                $is_removable{$coderef_name} = $coderef_pkg eq $package ? 1 : 0;
              }
              else {
                  $sub = do {
                      \&{ $package . '::' . $name };
                  };
  
 +                if ( !defined(&$sub) ) {
 +                    Carp::cluck
 +                        "Trying to export undefined sub ${package}::${name}";
 +                    next;
 +                }
 +
                  $is_removable{$name} = 1;
 +                $coderef_name = $name;
              }
  
              $export_recorder->{$sub} = 1;
  
 -            $exports{$name} = sub {$sub};
 +            $exports{$coderef_name} = sub {$sub};
          }
-         for my $name ( keys %{ $args->{groups} } ) {
-             my $group = $args->{groups}{$name};
-             if (ref $group eq 'CODE') {
-                 $groups{$name} = $class->_make_wrapped_group(
-                     $package,
-                     $group,
-                     $export_recorder,
-                     \%exports,
-                     \%is_removable
-                 );
-             }
-             elsif (ref $group eq 'ARRAY') {
-                 $groups{$name} = $group;
-             }
-         }
      }
  
-     return ( \%exports, \%is_removable, \%groups );
+     return ( \%exports, \%is_removable );
  }
  
  our $CALLER;
@@@ -635,14 -588,15 +608,15 @@@ Moose::Exporter - make an import() and 
    use Moose::Exporter;
  
    Moose::Exporter->setup_import_methods(
-       with_caller => [ 'has_rw', 'sugar2' ],
-       as_is       => [ 'sugar3', \&Some::Random::thing ],
-       also        => 'Moose',
+       with_meta => [ 'has_rw', 'sugar2' ],
+       as_is     => [ 'sugar3', \&Some::Random::thing ],
+       also      => 'Moose',
    );
  
    sub has_rw {
-       my ($caller, $name, %options) = @_;
-       Class::MOP::class_of($caller)->add_attribute($name,
+       my ( $meta, $name, %options ) = @_;
+       $meta->add_attribute(
+           $name,
            is => 'rw',
            %options,
        );
@@@ -705,12 -659,13 +679,13 @@@ This method accepts the following param
  
  =over 8
  
- =item * with_caller => [ ... ]
+ =item * with_meta => [ ... ]
  
  This list of function I<names only> will be wrapped and then exported. The
- wrapper will pass the name of the calling package as the first argument to the
- function. Many sugar functions need to know their caller so they can get the
- calling package's metaclass object.
+ wrapper will pass the metaclass object for the caller as its first argument.
+ Many sugar functions will need to use this metaclass object to do something to
+ the calling package.
  
  =item * as_is => [ ... ]
  
@@@ -728,7 -683,7 +703,7 @@@ to keep it
  This is a list of modules which contain functions that the caller
  wants to export. These modules must also use C<Moose::Exporter>. The
  most common use case will be to export the functions from C<Moose.pm>.
- Functions specified by C<with_caller> or C<as_is> take precedence over
+ Functions specified by C<with_meta> or C<as_is> take precedence over
  functions exported by modules specified by C<also>, so that a module
  can selectively override functions exported by another module.
  
index 589883a,b3cca46..ef69fb2
mode 100755,100644..100644
@@@ -7,6 -7,7 +7,7 @@@ use Test::More tests => 45
  use Test::Exception;
  
  {
      package ExGroups1;
      use Moose::Exporter;
      use Moose ();
          with_meta   => ['with_meta1'],
          with_caller => ['default_export1'],
          as_is       => ['default_export2'],
-         groups      => { all_group  => [':all'], 
-                          just_one   => ['default_export1'] }
+         groups      => {
+             all_group => [':all'],
+             just_one  => ['default_export1']
+         }
      );
  
-     sub default_export1 { 1 }
-     sub default_export2 { 2 }
+     sub default_export1 {1}
+     sub default_export2 {2}
  
      sub with_meta1 (&) {
-         my ($meta, $code) = @_;
+         my ( $meta, $code ) = @_;
          return $meta;
      }
  }
  
  {
      package UseAllGroup;
-     
      ExGroups1->import(':all_group');
  
      ::can_ok( __PACKAGE__, 'with_meta1' );
  
      my $meta;
      eval q/$meta = with_meta1 { return 'coderef'; }/;
-     ::is($@, '', 'calling with_meta1 with prototype is not an error');
+     ::is( $@, '', 'calling with_meta1 with prototype is not an error' );
      ::isa_ok( $meta, 'Moose::Meta::Class', 'with_meta first argument' );
-     ::is( prototype( __PACKAGE__->can('with_meta1') ), 
-           prototype( ExGroups1->can('with_meta1') ),
-     'using correct prototype on with_meta function' );
+     ::is(
+         prototype( __PACKAGE__->can('with_meta1') ),
+         prototype( ExGroups1->can('with_meta1') ),
+         'using correct prototype on with_meta function'
+     );
  
      ExGroups1->unimport();
  
-     ::ok( ! __PACKAGE__->can('with_meta1'), __PACKAGE__.'::with_meta1() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('default_export1'), __PACKAGE__.'::default_export1() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('default_export2'), __PACKAGE__.'::default_export2() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('has'), __PACKAGE__.'::has() has been cleaned' );
+     ::ok( !__PACKAGE__->can('with_meta1'),
+         __PACKAGE__ . '::with_meta1() has been cleaned' );
+     ::ok( !__PACKAGE__->can('default_export1'),
+         __PACKAGE__ . '::default_export1() has been cleaned' );
+     ::ok( !__PACKAGE__->can('default_export2'),
+         __PACKAGE__ . '::default_export2() has been cleaned' );
+     ::ok( !__PACKAGE__->can('has'),
+         __PACKAGE__ . '::has() has been cleaned' );
  }
  
  {
      package UseJustOne;
  
      ExGroups1->import(':just_one');
  
      ::can_ok( __PACKAGE__, 'default_export1' );
-     ::ok( ! __PACKAGE__->can('default_export2'), __PACKAGE__.'::default_export2() was not imported' );
-     ::ok( ! __PACKAGE__->can('has'), __PACKAGE__.'::has() was not imported' );
+     ::ok( !__PACKAGE__->can('default_export2'),
+         __PACKAGE__ . '::default_export2() was not imported' );
+     ::ok( !__PACKAGE__->can('has'),
+         __PACKAGE__ . '::has() was not imported' );
  
      ExGroups1->unimport();
  
-     ::ok( ! __PACKAGE__->can('default_export1'), __PACKAGE__.'::default_export1() has been cleared' );
+     ::ok( !__PACKAGE__->can('default_export1'),
+         __PACKAGE__ . '::default_export1() has been cleared' );
  }
  
  {
      package ExGroups2;
      use Moose::Exporter;
-     
      Moose::Exporter->setup_import_methods(
          also        => ['ExGroups1'],
          as_is       => ['exgroups2_as_is'],
          with_caller => ['exgroups2_with_caller'],
-         groups      => { default    => ['exgroups2_as_is'],
-                          code_group => \&generate_group,
-                          parent1    => [qw(:ExGroups1 :code_group)],
-                          parent2    => [qw(:all)] }
+         groups      => {
+             default    => ['exgroups2_as_is'],
+             code_group => \&generate_group,
+             parent1    => [qw(:ExGroups1 :code_group)],
+             parent2    => [qw(:all)]
+         }
      );
  
-     sub exgroups2_as_is { 3 }
+     sub exgroups2_as_is {3}
  
 +    sub exgroups2_with_caller { 4 }
 +
      sub generate_group {
-         my ($caller, $group_name, $args, $context) = @_;
+         my ( $caller, $group_name, $args, $context ) = @_;
  
-         ::is($group_name, 'code_group', 'original name is passed to group code');
-         ::is($args->{install_as}, $caller . '_code', 'group code arguments match caller');
-         ::is($context->{from}, __PACKAGE__, 'defined package name is passed to group code');
+         ::is( $group_name, 'code_group',
+             'original name is passed to group code' );
+         ::is( $args->{install_as}, $caller . '_code',
+             'group code arguments match caller' );
+         ::is( $context->{from}, __PACKAGE__,
+             'defined package name is passed to group code' );
  
          return { $args->{install_as} => \&exported_by_group };
      }
  
      sub exported_by_group (&) {
-         my ($caller, $coderef) = @_;
+         my ( $caller, $coderef ) = @_;
          return $caller;
      }
  }
  
  {
      package UseDefault;
-     
      ExGroups2->import;
  
      ::can_ok( __PACKAGE__, 'exgroups2_as_is' );
-     ::ok( ! __PACKAGE__->can('exgroups2_with_caller'), '"default" group is no longer "all"' );
+     ::ok( !__PACKAGE__->can('exgroups2_with_caller'),
+         '"default" group is no longer "all"' );
  }
  
  {
      package UseCodeGroup;
  
-     ExGroups2->import(':code_group', { install_as => (my $export_name = __PACKAGE__.'_code') });
+     ExGroups2->import( ':code_group',
+         { install_as => ( my $export_name = __PACKAGE__ . '_code' ) } );
  
      ::can_ok( __PACKAGE__, $export_name );
-     ::ok( &UseCodeGroup_code() eq __PACKAGE__, 'code group exports act like "with_caller" subs' );
-     ::lives_ok(sub { UseCodeCodeGroup_code { return 'code block'; } }, 'code group exports keep their prototypes');
+     ::ok( &UseCodeGroup_code() eq __PACKAGE__,
+         'code group exports act like "with_caller" subs' );
+     ::lives_ok(
+         sub {
+             UseCodeCodeGroup_code { return 'code block'; };
+         },
+         'code group exports keep their prototypes'
+     );
  
-     ::ok( ! __PACKAGE__->can('exgroups2_as_is'), 'code group will not automatically export any symbols' );
+     ::ok( !__PACKAGE__->can('exgroups2_as_is'),
+         'code group will not automatically export any symbols' );
  
      ExGroups2->unimport;
-     
-     ::ok( ! __PACKAGE__->can($export_name), 
-         'dynamically-named '. __PACKAGE__."::$export_name() has been cleared" );
+     ::ok(
+         !__PACKAGE__->can($export_name),
+         'dynamically-named '
+             . __PACKAGE__
+             . "::$export_name() has been cleared"
+     );
  }
  
  {
      package UseParent1;
  
-     ExGroups2->import(':parent1', { install_as => (my $export_name = __PACKAGE__.'_code') });
+     ExGroups2->import( ':parent1',
+         { install_as => ( my $export_name = __PACKAGE__ . '_code' ) } );
  
      ::can_ok( __PACKAGE__, $export_name );
      ::can_ok( __PACKAGE__, 'default_export1' );
  
      ExGroups2->unimport;
  
-     ::ok( ! __PACKAGE__->can($export_name), __PACKAGE__."::$export_name() has been cleared" );
-     ::ok( ! __PACKAGE__->can('default_export1'), __PACKAGE__.'::default_export1() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('default_export2'), __PACKAGE__.'::default_export2() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('has'), __PACKAGE__.'::has() has been cleaned' );
+     ::ok( !__PACKAGE__->can($export_name),
+         __PACKAGE__ . "::$export_name() has been cleared" );
+     ::ok( !__PACKAGE__->can('default_export1'),
+         __PACKAGE__ . '::default_export1() has been cleaned' );
+     ::ok( !__PACKAGE__->can('default_export2'),
+         __PACKAGE__ . '::default_export2() has been cleaned' );
+     ::ok( !__PACKAGE__->can('has'),
+         __PACKAGE__ . '::has() has been cleaned' );
  }
  
  {
      package UseParent2;
  
-     ExGroups2->import(':parent2', { install_as => (my $export_name = __PACKAGE__.'_code') });
+     ExGroups2->import( ':parent2',
+         { install_as => ( my $export_name = __PACKAGE__ . '_code' ) } );
  
-     ::ok( ! __PACKAGE__->can($export_name), '"all" group will not call code groups' );
+     ::ok( !__PACKAGE__->can($export_name),
+         '"all" group will not call code groups' );
      ::can_ok( __PACKAGE__, 'exgroups2_as_is' );
      ::can_ok( __PACKAGE__, 'exgroups2_with_caller' );
      ::can_ok( __PACKAGE__, 'default_export1' );
  
      ExGroups2->unimport;
  
-     ::ok( ! __PACKAGE__->can('exgroups2_as_is'), __PACKAGE__.'::exgroups2_as_is() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('exgroups2_with_caller'), __PACKAGE__.'::exgroups2_with_caller() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('default_export1'), __PACKAGE__.'::default_export1() has been cleaned' );
-     ::ok( ! __PACKAGE__->can('has'), __PACKAGE__.'::has() has been cleaned' );
+     ::ok( !__PACKAGE__->can('exgroups2_as_is'),
+         __PACKAGE__ . '::exgroups2_as_is() has been cleaned' );
+     ::ok( !__PACKAGE__->can('exgroups2_with_caller'),
+         __PACKAGE__ . '::exgroups2_with_caller() has been cleaned' );
+     ::ok( !__PACKAGE__->can('default_export1'),
+         __PACKAGE__ . '::default_export1() has been cleaned' );
+     ::ok( !__PACKAGE__->can('has'),
+         __PACKAGE__ . '::has() has been cleaned' );
  }
  
@@@ -8,7 -8,7 +8,7 @@@ use Test::Exception
  BEGIN {
      eval "use Test::Output;";
      plan skip_all => "Test::Output is required for this test" if $@;
-     plan tests => 49;
 -    plan tests => 63;
++    plan tests => 65;
  }
  
  
      use Moose ();
  
      sub wrapped1 {
-         my $caller = shift;
-         return $caller . ' called wrapped1';
+         my $meta = shift;
+         return $meta->name . ' called wrapped1';
      }
  
      Moose::Exporter->setup_import_methods(
-         with_caller => ['wrapped1'],
-         also        => 'Moose',
+         with_meta => ['wrapped1'],
+         also      => 'Moose',
      );
  }
  
  }
  
  {
 +    package NonExistentExport;
 +
 +    use Moose ();
 +
 +    ::stderr_like {
 +        Moose::Exporter->setup_import_methods(
 +            also => ['Moose'],
 +            with_caller => ['does_not_exist'],
 +        );
 +    } qr/^Trying to export undefined sub NonExistentExport::does_not_exist/,
 +      "warns when a non-existent method is requested to be exported";
 +}
 +
 +{
 +    package WantsNonExistentExport;
 +
 +    NonExistentExport->import;
 +
 +    ::ok(!__PACKAGE__->can('does_not_exist'),
 +         "undefined subs do not get exported");
++
+     package AllOptions;
+     use Moose ();
+     use Moose::Exporter;
+     Moose::Exporter->setup_import_methods(
+         also        => ['Moose'],
+         with_meta   => [ 'with_meta1', 'with_meta2' ],
+         with_caller => [ 'with_caller1', 'with_caller2' ],
+         as_is       => ['as_is1'],
+     );
+     sub with_caller1 {
+         return @_;
+     }
+     sub with_caller2 (&) {
+         return @_;
+     }
+     sub as_is1 {2}
+     sub with_meta1 {
+         return @_;
+     }
+     sub with_meta2 (&) {
+         return @_;
+     }
+ }
+ {
+     package UseAllOptions;
+     AllOptions->import();
+ }
+ {
+     can_ok( 'UseAllOptions', $_ )
+         for qw( with_meta1 with_meta2 with_caller1 with_caller2 as_is1 );
+     {
+         my ( $caller, $arg1 ) = UseAllOptions::with_caller1(42);
+         is( $caller, 'UseAllOptions', 'with_caller wrapped sub gets the right caller' );
+         is( $arg1, 42, 'with_caller wrapped sub returns argument it was passed' );
+     }
+     {
+         my ( $meta, $arg1 ) = UseAllOptions::with_meta1(42);
+         isa_ok( $meta, 'Moose::Meta::Class', 'with_meta first argument' );
+         is( $arg1, 42, 'with_meta1 returns argument it was passed' );
+     }
+     is(
+         prototype( UseAllOptions->can('with_caller2') ),
+         prototype( AllOptions->can('with_caller2') ),
+         'using correct prototype on with_meta function'
+     );
+     is(
+         prototype( UseAllOptions->can('with_meta2') ),
+         prototype( AllOptions->can('with_meta2') ),
+         'using correct prototype on with_meta function'
+     );
+     {
+         package UseAllOptions;
+         AllOptions->unimport();
+     }
+     ok( ! UseAllOptions->can($_), "UseAllOptions::$_ has been unimported" )
+         for qw( with_meta1 with_meta2 with_caller1 with_caller2 as_is1 );
  }