Tree-Authz/lib/Tree/Authz/Role.pm
package Tree::Authz::Role;
use strict;
use warnings;
use Carp;
use overload '""' => 'name';
# NOTE: if this class ever holds any more data than just its name, it
# should probably be a singleton
=over
=item new( $role, $authz_class )
Represents a role.
=cut
# called by Tree::Authz::role
sub new {
my ($proto, $role, $authz_class) = @_;
my $self = [ $role, $authz_class ];
bless $self, ref( $proto ) || $proto;
# $self->_init;
return $self;
}
=item name()
Returns the name of this role.
=item group_name()
DEPRECATED.
Use C<name> instead.
=cut
sub name { $_[0]->[0] }
=item authz
Returns the L<Tree::Authz> subclass used to instantiate this role.
=cut
sub authz { $_[0]->[1] }
sub group_name {
carp "'group_name' is deprecated - use 'name' instead";
goto &name;
}
=item list_roles
Returns a list of roles inherited by this role, including this role.
=cut
sub list_roles {
my ($self) = @_;
my @my_roles = grep { $self->can( $_ ) } $self->authz->list_roles;
wantarray ? @my_roles : [ @my_roles ];
}
=item setup_permissions( $cando )
Instance method.
Adds methods to the class representing the role. I<$cando> is a single method
name, or arrayref of method names. No-op methods are added to the class
representing the group:
my $spies = $authz->role( 'spies' );
my $cando = [ qw( read_secret wear_disguise ) ];
$spies->setup_permissions( $cando );
if ( $spies->can( 'read_secret' ) ) {
warn 'Compromised!';
}
warn 'Trust no-one' if $spies->can( 'wear_disguise' );
=cut
sub setup_permissions {
my ($self, $cando) = @_;
croak( 'Nothing to permit' ) unless $cando;
my $class = ref( $self ) || croak( 'object method called on class name' );
$class->_setup_perms( $cando );
}
sub _setup_perms {
my ($class, $cando) = @_;
my @permits = ref( $cando ) ? @$cando : ( $cando );
no strict 'refs';
foreach my $permit ( @permits ) {
*{"${class}::$permit"} = sub {};
}
}
=item setup_abilities( $name => $coderef, [ $name2 => $coderef2 ], ... )
Instance method.
Adds methods to the class representing the group. Keys give method names and
values are coderefs that will be installed as methods on the group class:
my $spies = $authz->get_group( 'spies' );
my %able = ( read_secret => sub {
my ($self, $file) = @_;
open( SECRET, $file );
local $/;
<SECRET>;
},
find_moles => sub { ... },
);
$spies->setup_abilities( %able );
if ( $spies->can( 'read_secret' ) ) {
print $spies->read_secret( '/path/to/secret/file' );
}
# or
if ( my $read = $spies->can( 'read_secret' ) ) {
print $spies->$read( '/path/to/secret/file' );
}
# with an unknown $group
my $get_secret = $group->can( 'read_secret' ) || # spy
$group->can( 'steal_document' ) || # mole
$group->can( 'create_secret' ) || # spymaster
$group->can( 'do_illicit_thing' ) || # politician
sub {}; # boring life
my $secret = $group->$get_secret;
=cut
sub setup_abilities {
my ($self, %code) = @_;
croak( 'Nothing to set up' ) unless %code;
my $class = ref( $self ) || croak( 'object method called on class name' );
$class->_setup_abil( %code );
}
sub _setup_abil {
my ($class, %code) = @_;
no strict 'refs';
foreach my $method ( keys %code ) {
*{"${class}::$method"} = $code{ $method };
}
}
=item setup_plugins( $plugins )
Instance method.
Instead of adding a set of coderefs to a group's class, this method adds
a class to the C<@ISA> array of the group's class.
package My::Spies;
sub wear_disguise {}
sub read_secret {
my ($self, $file) = @_;
open( SECRET, $file );
local $/;
<SECRET>;
}
package main;
my $spies = $authz->get_group( 'spies' );
$spies->setup_plugins( 'My::Spies' );
if ( $spies->can( 'read_secret' ) ) {
warn 'Compromised!';
print $spies->read_secret( '/path/to/secret/file' );
}
warn 'Trust no-one' if $spies->can( 'wear_disguise' );
=back
=cut
sub setup_plugins {
my ($self, $plugins) = @_;
croak( 'Nothing to plug in' ) unless $plugins;
my $class = ref( $self ) || croak( 'object method called on class name' );
$class->_setup_plugins( $plugins );
}
sub _setup_plugins {
my ($class, $plugins) = @_;
my @plugins = ref( $plugins ) ? @$plugins : ( $plugins );
no strict 'refs';
push( @{"${class}::ISA"}, $_ ) for @plugins;
}
1;