Jojo-Role

 view release on metacpan or  search on metacpan

lib/Jojo/Role/Tiny.pm  view on Meta::CPAN

our $VERSION = '2.000006';
$VERSION =~ tr/_//d;

# Aliasing of Role::Tiny symbols
BEGIN {
  *INFO           = \%Role::Tiny::INFO;
  *APPLIED_TO     = \%Role::Tiny::APPLIED_TO;
  *COMPOSED       = \%Role::Tiny::COMPOSED;
  *COMPOSITE_INFO = \%Role::Tiny::COMPOSITE_INFO;
  *ON_ROLE_CREATE = \@Role::Tiny::ON_ROLE_CREATE;
}

our %INFO;
our %APPLIED_TO;
our %COMPOSED;
our %COMPOSITE_INFO;
our @ON_ROLE_CREATE;

# Module state workaround totally stolen from Zefram's Module::Runtime.

BEGIN {
  *_WORK_AROUND_BROKEN_MODULE_STATE = "$]" < 5.009 ? sub(){1} : sub(){0};
  *_WORK_AROUND_HINT_LEAKAGE
    = "$]" < 5.011 && !("$]" >= 5.009004 && "$]" < 5.010001)
      ? sub(){1} : sub(){0};
  *_MRO_MODULE = "$]" < 5.010 ? sub(){"MRO/Compat.pm"} : sub(){"mro.pm"};
}

sub croak {
  require Carp;
  no warnings 'redefine';
  *croak = \&Carp::croak;
  goto &Carp::croak;
}

sub Jojo::Role::Tiny::__GUARD__::DESTROY {
  delete $INC{$_[0]->[0]} if @{$_[0]};
}

sub _load_module {
  my ($module) = @_;
  (my $file = "$module.pm") =~ s{::}{/}g;
  return 1
    if $INC{$file};

  # can't just ->can('can') because a sub-package Foo::Bar::Baz
  # creates a 'Baz::' key in Foo::Bar's symbol table
  return 1
    if grep !/::\z/, keys %{_getstash($module)};
  my $guard = _WORK_AROUND_BROKEN_MODULE_STATE
    && bless([ $file ], 'Jojo::Role::Tiny::__GUARD__');
  local %^H if _WORK_AROUND_HINT_LEAKAGE;
  require $file;
  pop @$guard if _WORK_AROUND_BROKEN_MODULE_STATE;
  return 1;
}

sub import {
  my $target = caller;
  my $me = shift;
  strict->import;
  warnings->import;
  $me->_install_subs($target);
  $me->make_role($target);
}

sub make_role {
  my ($me, $target) = @_;
  return if $me->is_role($target); # already exported into this package
  $INFO{$target}{is_role} = 1;
  # get symbol table reference
  my $stash = _getstash($target);
  # grab all *non-constant* (stash slot is not a scalarref) subs present
  # in the symbol table and store their refaddrs (no need to forcibly
  # inflate constant subs into real subs) with a map to the coderefs in
  # case of copying or re-use
  my @not_methods = map +(ref $_ eq 'CODE' ? $_ : ref $_ ? () : *$_{CODE}||()), values %$stash;
  @{$INFO{$target}{not_methods}={}}{@not_methods} = @not_methods;
  # a role does itself
  $APPLIED_TO{$target} = { $target => undef };
  foreach my $hook (@ON_ROLE_CREATE) {
    $hook->($target);
  }
}

sub _install_subs {
  my ($me, $target) = @_;
  return if $me->is_role($target);
  # install before/after/around subs
  foreach my $type (qw(before after around)) {
    *{_getglob "${target}::${type}"} = sub {
      push @{$INFO{$target}{modifiers}||=[]}, [ $type => @_ ];
      return;
    };
  }
  *{_getglob "${target}::requires"} = sub {
    push @{$INFO{$target}{requires}||=[]}, @_;
    return;
  };
  *{_getglob "${target}::with"} = sub {
    $me->apply_roles_to_package($target, @_);
    return;
  };
}

sub role_application_steps {
  qw(_install_methods _check_requires _install_modifiers _copy_applied_list);
}

sub apply_single_role_to_package {
  my ($me, $to, $role) = @_;

  _load_module($role);

  croak "This is apply_role_to_package" if ref($to);
  croak "${role} is not a Role::Tiny" unless $me->is_role($role);

  foreach my $step ($me->role_application_steps) {
    $me->$step($to, $role);
  }
}



( run in 2.035 seconds using v1.01-cache-2.11-cpan-ad19def0cd9 )