use strict;
use warnings FATAL => 'all';
-our $VERSION = '1.003000'; # 1.3.0
+our $VERSION = '1.003002'; # 1.3.2
$VERSION = eval $VERSION;
our %INFO;
my $me = shift;
strict->import;
warnings->import(FATAL => 'all');
- return if $INFO{$target}; # already exported into this package
+ return if $me->is_role($target); # already exported into this package
$INFO{$target}{is_role} = 1;
# get symbol table reference
my $stash = _getstash($target);
_load_module($role);
die "This is apply_role_to_package" if ref($to);
- die "${role} is not a Role::Tiny" unless $INFO{$role};
+ die "${role} is not a Role::Tiny" unless $me->is_role($role);
foreach my $step ($me->role_application_steps) {
$me->$step($to, $role);
$object;
}
+my $role_suffix = 'A000';
+sub _composite_name {
+ my ($me, $superclass, @roles) = @_;
+
+ my $new_name = join(
+ '__WITH__', $superclass, my $compose_name = join '__AND__', @roles
+ );
+
+ if (length($new_name) > 252) {
+ $new_name = $COMPOSED{abbrev}{$new_name}
+ ||= substr($new_name, 0, 250 - length $role_suffix).'__'.$role_suffix++;
+ }
+ return wantarray ? ($new_name, $compose_name) : $new_name;
+}
+
sub create_class_with_roles {
my ($me, $superclass, @roles) = @_;
}
}
- my $new_name = join(
- '__WITH__', $superclass, my $compose_name = join '__AND__', @roles
- );
+ my ($new_name, $compose_name) = $me->_composite_name($superclass, @roles);
return $new_name if $COMPOSED{class}{$new_name};
foreach my $role (@roles) {
_load_module($role);
- die "${role} is not a Role::Tiny" unless $INFO{$role};
+ die "${role} is not a Role::Tiny" unless $me->is_role($role);
}
if ($] >= 5.010) {
return $me->apply_role_to_package($to, $roles[0]) if @roles == 1;
my %conflicts = %{$me->_composite_info_for(@roles)->{conflicts}};
- delete $conflicts{$_} for keys %{ $me->_concrete_methods_of($to) };
+ my @have = grep $to->can($_), keys %conflicts;
+ delete @conflicts{@have};
+
if (keys %conflicts) {
my $fail =
join "\n",
die $fail;
}
+ # conflicting methods are supposed to be treated as required by the
+ # composed role. we don't have an actual composed role, but because
+ # we know the target class already provides them, we can instead
+ # pretend that the roles don't do for the duration of application.
+ my @role_methods = map $me->_concrete_methods_of($_), @roles;
+ # separate loops, since local ..., delete ... for ...; creates a scope
+ local @{$_}{@have} for @role_methods;
+ delete @{$_}{@have} for @role_methods;
+
# the if guard here is essential since otherwise we accidentally create
# a $INFO for something that isn't a Role::Tiny (or Moo::Role) because
# autovivification hates us and wants us to die()
sub methods_provided_by {
my ($me, $role) = @_;
- die "${role} is not a Role::Tiny" unless my $info = $INFO{$role};
- (keys %{$me->_concrete_methods_of($role)}, @{$info->{requires}||[]});
+ die "${role} is not a Role::Tiny" unless $me->is_role($role);
+ (keys %{$me->_concrete_methods_of($role)}, @{$INFO{$role}->{requires}||[]});
}
sub _install_methods {
my $FALLBACK = sub { 0 };
sub _install_does {
my ($me, $to) = @_;
-
+
# only add does() method to classes
- return if $INFO{$to};
-
+ return if $me->is_role($to);
+
# add does() only if they don't have one
*{_getglob "${to}::does"} = \&does_role unless $to->can('does');
-
- return if ($to->can('DOES') and $to->can('DOES') != (UNIVERSAL->can('DOES') || 0));
-
+
+ return
+ if $to->can('DOES') and $to->can('DOES') != (UNIVERSAL->can('DOES') || 0);
+
my $existing = $to->can('DOES') || $to->can('isa') || $FALLBACK;
my $new_sub = sub {
my ($proto, $role) = @_;
sub is_role {
my ($me, $role) = @_;
- return !!$INFO{$role};
+ return !!($INFO{$role} && $INFO{$role}{is_role});
}
1;
=head1 IMPORTED SUBROUTINES
+In addition to importing subroutines, using C<Role::Tiny> applies L<strict> and
+L<fatal warnings|perllexwarn/Fatal Warnings> to the caller.
+
=head2 requires
requires qw(foo bar);
tobyink - Toby Inkster (cpan:TOBYINK) <tobyink@cpan.org>
+haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>
+
=head1 COPYRIGHT
Copyright (c) 2010-2012 the Role::Tiny L</AUTHOR> and L</CONTRIBUTORS>