Rose-Object

 view release on metacpan or  search on metacpan

lib/Rose/Object/MakeMethods.pm  view on Meta::CPAN

package Rose::Object::MakeMethods;

use strict;

use Carp();

our $VERSION = '0.856';

__PACKAGE__->allow_apparent_reload(1);

our %Made_Method_Custom;

sub import
{
  my($class) = shift;

  return 1  unless(@_);

  my($options, $args) = $class->_normalize_args(@_);

  $options->{'target_class'} ||= (caller)[0];

  $class->make_methods($options, $args);

  return 1;
}

sub make_methods
{
  my($class) = shift;

  my($options, $args) = $class->_normalize_args(@_);

  $options->{'target_class'} ||= (caller)[0];  

  #use Data::Dumper;
  #print STDERR Dumper($options);
  #print STDERR Dumper($args);

  while(@$args)
  {
    $class->__make_methods($options, shift(@$args), shift(@$args));
  }

  return 1;
}

# Can't use the class method maker easily here due to a chicken/egg
# situation, so this code is manually inlined.
my %Inheritable_Scalar;

sub allow_apparent_reload
{
  my($class) = ref($_[0]) ? ref(shift) : shift;

  if(@_)
  {
    return $Inheritable_Scalar{$class}{'allow_apparent_reload'} = shift;
  }

  return $Inheritable_Scalar{$class}{'allow_apparent_reload'}
    if(exists $Inheritable_Scalar{$class}{'allow_apparent_reload'});

  my @parents = ($class);

  while(my $parent = shift(@parents))
  {
    no strict 'refs';
    foreach my $subclass (@{$parent . '::ISA'})
    {
      push(@parents, $subclass);

      if(exists $Inheritable_Scalar{$subclass}{'allow_apparent_reload'})
      {
        return $Inheritable_Scalar{$subclass}{'allow_apparent_reload'}
      }
    }
  }

  return undef;
}

# XXX: This nasty hack should be unneeded now and will probably
# XXX: be removed some time in the future.
our $Preserve_Existing = 0;

sub __make_methods
{
  my($class) = shift;

  #my $options;

  #if(ref $_[0] eq 'HASH')
  #{
  #  $options = shift;
  #}
  #else { $options = {} }

  #$options->{'target_class'} ||= (caller)[0];  

  my $options     = shift;
  my $method_type = shift;
  my $methods     = shift;

  my $target_class = $options->{'target_class'};

  while(@$methods)
  {
    my $method_name = shift(@$methods);
    my $method_args = shift(@$methods);

    my $make = $class->$method_type($method_name => $method_args, $options ||= {});

    Carp::croak "${class}::method_type(...) didn't return a hash ref!"
      unless(ref $make eq 'HASH');

    no strict 'refs';    

    METHOD: while(my($name, $code) = each(%$make))
    {
      Carp::croak "${class}::method_type(...) - key for $name is not a code ref!"
        unless(ref $code eq 'CODE' || (ref $code eq 'HASH' && $code->{'make_method'}));

      if(my $code = $target_class->can($name))
      {
        if($options->{'preserve_existing'} || $Preserve_Existing)
        {
          next METHOD;
        }

        unless($options->{'override_existing'})
        {
          if($class->allow_apparent_reload && $class->apparently_made_method($code))
          {
            next METHOD;
          }

          Carp::croak "Cannot create method ${target_class}::$name - method already exists";
        }
      }

      no warnings;

      if(ref $code eq 'CODE')
      {
        *{"${target_class}::$name"} = $code;
      }
      else
      {
        # XXX: Must track these separately because they do not show up as
        # XXX: being named __ANON__ when fetching the sub_identity()
        $Made_Method_Custom{$target_class}{$name}++;
        $code->{'make_method'}($name, $target_class, $options);
      }
    }
  }

  return 1;
}



( run in 1.444 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )