Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Re: OO - best way to have protected methods

by radiantmatrix (Parson)
on Aug 17, 2005 at 18:21 UTC ( [id://484506]=note: print w/replies, xml ) Need Help??


in reply to OO - best way to have protected methods

Just sort of "thinking out loud", here...

package Private; use strict; use warnings; use Carp; use vars qw'@ISA @EXPORT $VERSION $PACKAGE'; #===================================================================== +========= # Private.pm - a module to implement extremely simple private subs # this is a 'stub', which will hopefully be expanded upon + by # others. #--------------------------------------------------------------------- +--------- # use Private; # sub _my_private_sub { # is_private; # makes this sub package-private # } #--------------------------------------------------------------------- +--------- # (c)2005 Darren Meyer <darren.meyer@gmail.com> (see LICENSE at end of + file) #===================================================================== +========= $VERSION = '0.81'; require Exporter; @ISA = 'Exporter'; @EXPORT = 'is_private'; ## automatically set up the invoking package name on 'use' my $PACKAGE = caller(0); sub is_private() { my @call = caller(1); ## ok if caller of sub that called us was in the invoking package return 1 if $call[0] eq $PACKAGE; ## otherwise, create fatal error ## Package_Name cannot call sub_name (private to Other_Package) confess ( $call[0].' cannot call '.$call[3].' (private to '.$PACKAGE.')' +."\n" ); } 1; __END__ =head1 LICENSE This module is licensed under the MIT License, as stated here: Copyright (c)2005 Darren Meyer Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the ``Software''), t +o deal in the Software without restriction, including without limitation the rig +hts to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnish +ed to do so, subject to the following conditions: The above copyright notice and this permission notice shall be include +d in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPR +ESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILIT +Y, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHAL +L THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISIN +G FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. =cut

With the above in place, you could create your private add sub like:

use Private; sub _add { is_private; ## die if called outside of this package my $self = shift; my $amount = shift; $self->balance($self->balance+$amount); }
<-radiant.matrix->
Larry Wall is Yoda: there is no try{} (ok, except in Perl6; way to ruin a joke, Larry! ;P)
The Code that can be seen is not the true Code
"In any sufficiently large group of people, most are idiots" - Kaa's Law

Replies are listed 'Best First'.
Re^2: OO - best way to have protected methods
by jdporter (Paladin) on Aug 17, 2005 at 19:37 UTC
    As currently implemented, that only works in the first module that calls is_private. Witness:
    package Foo; use Private; sub foo { is_private; } package Bar; use Private; sub bar { is_private; } package main; Bar->bar;
    main cannot call Bar::bar (private to Foo)

      Yeah, that module took me all of five minutes. It works if you use or require several different modules, but not if you create several in the same file. I don't know why this is, and therefore I don't know how to fix it. Patches and/or explanations would be welcome.

      And, BTW, it's not the first package to call is_private, but rather the first to use Private -- the init is done (and the 'private' package name determined) during import.

      I don't really have the drive to create something to solve this problem, as I don't really consider it a problem: I use the suggested syntax for methods/subs/variables I wish to be private (prepend the name with an underscore), and document that these are not intended for consumption outside the module code.

      If people want to shoot themselves in the foot by using subs/etc. I've marked as "I want this to be private", then I have no problem letting them. ;-)

      <-radiant.matrix->
      Larry Wall is Yoda: there is no try{} (ok, except in Perl6; way to ruin a joke, Larry! ;P)
      The Code that can be seen is not the true Code
      "In any sufficiently large group of people, most are idiots" - Kaa's Law
        The problem, of course, is in the
        my $PACKAGE = caller(0);
        happening at package level.

        Below is an implementation of your approach which is entirely scalable. It doesn't "remember" anything from 'import' time; it simply compares the package of the caller to the package of the callee, and requires that they be the same.

        package Private; use Exporter; @ISA = 'Exporter'; @EXPORT = 'is_private'; use strict; use warnings; use Carp; sub is_private() { my %c0; @c0{qw( pkg fn l sub )} = caller 0; my %c1; @c1{qw( pkg fn l sub )} = caller 1; if ( $c0{'pkg'} ne $c1{'pkg'} ) # throw an exception: { my %c2; @c2{qw( pkg fn l sub )} = caller 2; my $caller = $c2{'sub'} || $c2{'pkg'} || 'main'; croak "$caller cannot call $c1{'sub'} (private to $c0{'pkg'})" +; } } 1;
        Here's a little test rig.
        { package Parent; use Private; sub private_method { is_private; print "Private method calleed OK.\n +" } sub public_method { $_[0]->private_method } } { package Child; use base 'Parent'; sub child_calling_private { $_[0]->private_method } sub child_calling_public { $_[0]->public_method } } package main; print "\nBase class: call private method directly:\n"; eval { Parent->private_method }; $@ and print $@; print "\nBase class: call public method that calls private method:\n"; eval { Parent->public_method; }; $@ and print $@; print "\nDerived class: call parent's private method directly:\n"; eval { Child->private_method }; $@ and print $@; print "\nDerived class: call parent's public method that calls private + method:\n"; eval { Child->public_method; }; $@ and print $@; print "\nDerived class: call method that calls parent's private method +:\n"; eval { Child->child_calling_private; }; $@ and print $@; print "\nDerived class: call method that calls parent's public method: +\n"; eval { Child->child_calling_public; }; $@ and print $@;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://484506]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2024-04-24 18:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found