CTSMan has asked for the wisdom of the Perl Monks concerning the following question:

I have a class called XYZServer that I want to use to intercept (and divert) calls to another package my XYZServer package looks like this....
sub new { my $proto = shift; # Get the Prototype my $class = ref ($proto) || $proto; # Make the Class Name my $parent= ref ($proto) && $proto; # Get the Parent Class + (if Any) my $self = {}; # Create the HASH for +the Object bless($self, $class); # Create the Object $self->{_PARENT}= $parent; $self->{_RESULT} = ""; # Result of the method + call $self->{_STATUS} = 0; # Status Return (0 = O +K) $self->{SERVER} = XXX->new(); return $self; # Return the Object Ref +erence } sub allowed_function { my $self = shift; my $function = shift; if ($function eq "sane") { return(1) ; } else { return(0); } } sub AUTOLOAD { my $self = shift; my $thing = $AUTOLOAD; $thing =~ s/.*:://g; return if $AUTOLOAD =~ /DESTROY$/; if ($self->allowed_function($thing) { $self->{SERVER}->$AUTOLOAD(@_); } }
If I have a method called 'sane', and one called 'insane' that is defined in the XXX module, I want to do the following
my $X = XYZServer->new; my $sane; $sane = $X->sane;
I want the routine to execute, but if I say
$sane = $X->insane;
I want it not to execute the routine !! I have been racking my brain oh how to do this, but it has me stumped.

Replies are listed 'Best First'.
Re: Filtering access to an Objects functions
by steves (Curate) on Feb 07, 2003 at 18:48 UTC

    While I tend to agree with merlyn Perl will let you do whatever you want. But remember that someone has likely already done it. Have you looked at the protect module on CPAN? It gives you run-time access exceptions in a similar vein to those you get with C++ or Java at compile time.

    I haven't used it myself so no negative votes for that. 8-)

Re: Filtering access to an Objects functions
by adrianh (Chancellor) on Feb 07, 2003 at 21:27 UTC

    If you want to delegate some methods to another object Class::Delegation can be of great help. For example if we have:

    { package Foo; sub new { bless {}, shift }; sub sane { 42 }; sub insane { 666 }; };

    We can make a SaneFoo class that forwards calls to the sane method to an internal Foo object like this:

    { package SaneFoo; use Class::Delegation send => 'sane', to => 'Foo'; sub new { bless { Foo => Foo->new }, shift; }; };

    Which does what we want:

    use Test::More tests => 3; use Test::Exception 0.15; isa_ok my $o = SaneFoo->new, 'SaneFoo'; lives_and {is $o->sane, 42} 'sane worked'; dies_ok {$o->insane} 'insane failed'; __END__ # test results are: 1..3 ok 1 - The object isa SaneFoo ok 2 - sane worked ok 3 - insane failed

    If you are worried about people burrowing into the SaneFoo object and extracting the Foo object from the hash you can always use Abigail-II's inside out objects. Using this style the SaneFoo class could be implemented like this:

    { package SaneFoo; use Carp; my %Foo = (); sub new { my $self = bless {}, shift; $Foo{$self} = Foo->new; return($self); }; sub allows { my ($self, $method) = @_; return( $method eq "sane" ); }; sub AUTOLOAD { my $self = shift; our $AUTOLOAD; my ($method) = ($AUTOLOAD =~ m/([^:]+)$/); croak "$self cannot $method" unless $self->allows($method); $Foo{$self}->$method; }; sub DESTROY { my $self = shift; delete $Foo{$self}; }; };

    Hope this helps.

•Re: Filtering access to an Objects functions
by merlyn (Sage) on Feb 07, 2003 at 18:33 UTC
    If you want Java or C++, you know where to find them. Perl takes a different approach by default. Says the manpage:
    Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun.

    While you can build code that would reject arbitrary method calls depending on filename, location, superclass, or secret handshake, the moment I got your code I would rip all of that stuff back out.

    So, please, don't do that. It hurts.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      I've actually done that for (what I consider to be) a good reason: access controls on objects accessible via SOAP::Lite.

      In this case, I build a proxy object. All of my accessable methods had sub attributes that gave the permissions they needed and I added authentication tokens used to identify users through sessions.

      More details in my journal.

      So, please, don't do that. It hurts.

      Slightly unfair - since we don't know why it's needed. I can think of sensible reasons for doing this sort of thing.

      For example, I've done something similar when I have had a Template Toolkit based system with "user" and "admin" views of the same structures.

      To prevent "user" templates being able to do "admin" things by accident I created separate Admin and User classes that delegated a suitable subset of methods to the underlying object.

      By only supplying the appropriate plugins to the appropriate templates I get a far safer system and can let the designers go play with the templates without a care in the world.