in reply to RE: RE: Scoping and Objects
in thread Scoping and Objects

Maybe I didn't articulate well enough. I understand how to call the refrenced function, in fact I did that with a test. The problem is when that refrenced function, defined in a file separate and independant of the RadCache.pm file and stored as a refrence inside a property of the object, wants to act as a method of the object itself.
Hmm...maybe I should just post my code...Here:
package RadCache;

use Carp;
use vars qw($AUTOLOAD);

my %properties = (NAME => "Default", DIRS => {}, SPECIAL_FNS => {}, CACHE => {}, DRIVERS => {});

##
# Constructor
# Takes any of the above properties as arguements in the form of PROPERTY => value.
# Does not need any initial arguments
# Returns a new Cache object
##
sub new {
	my $caller = shift;
	my %args = @_;

	print "Creating new cache...\n";
	my $class = ref($caller) || $caller;
	my $self = {
		_permitted => \%properties,
		%properties,
	};
	foreach my $item (keys %args) {
		if (!($self->{_permitted}->{$item})) {
			print "ERROR: $item is not a valid input argument. Ignoring...\n";
			next;
		} else {
			$self->{$item} = $args{$item};
		}
	}
	bless $self, $class;
	print "COMMENT: Created Cache : " . $self->{NAME} . "\n";
	return $self;
}

# CLASS METHODS
--snip--
# INSTANCE METHODS
##
# LoadSpecialBuilder
# Takes a list of builder functions specific to a named directory alias
# used if you don't want the default builder function to run on your aliased directory
# form is: $myCache->LoadSpecialBuilder(MYALIAS1 => \&specBFunc1, ..., MYALIASN => \&specBFuncN);
# No Return Value
# Requires that you have an ALIASED directory named in the DIRS hash.
##
sub LoadSpecialBuilder {
	my $self = shift;
	my %args = @_;
	unless (ref $self) {
		croak "bad method call";
	}
	print "COMMENT: Loading Special Builder Function...\n";
	foreach my $item (keys %args) {
		$self->{SPECIAL_FNS}->{$item} = $args{$item};
		print "COMMENT: Added special builder function for $item\n";
	}
}
##
# UnLoadSpecialBuilder
# Takes a list of aliases and removes them from the SpecialFunc hash in the Cache
# NOTE: REMOVING A Special Function  MEANS reverting to the default builder function
# form is: $myCache->UnLoadSpecialBuilder(MYALIAS1, MYALIAS2,...,MYALIASN);
# No Return Value
##
sub UnLoadSpecialBuilder {
	my $self = shift;
	my @args = @_;
	unless (ref $self) {
		croak "bad method call";
	}
	print "COMMENT: UnLoading Special Builder Function...\n";
	foreach my $item (@args) {
		if ($self->{SPECIAL_FNS}->{$item}) { # if the item is really in the DIRS alias hash, then delete it
			delete($self->{SPECIAL_FNS}->{$item});
			print "COMMENT: Special builder function for $item has been deleted\n";
		} else {
			print "ERROR: can't delete a non-existant alias!\n";
			next;
		}
	}
}
##
# Build
# Takes no arguments
# Does the labor of calling special builder functions and for each non-special one
#   does a default build
# DEFAULT: just takes the first tier of element names from the ALIASED Directory if it is infact a directory
# Fills in the CACHE property with the correct datastructure of :
# 				ALIAS => STRUCTURE
# STRUCTURE is dependant on the build. for DEFAULT build:
#    			ALIAS => STRING
# 				where STRING is a string of all the elements of a directory separated by a " "
# No Return Value
##
sub Build {
	my $self = shift;
	my @args = @_;
	unless (ref $self) {
		croak "bad method call";
	}
	print "COMMENT: Building the Cache...\n";

	foreach my $dir_alias (keys(%{$self->{DIRS}})) {
		if ($self->{SPECIAL_FNS}->{$dir_alias}) {
			print "COMMENT: executing SPECIAL for $dir_alias...\n";
			&{$self->{SPECIAL_FNS}->{$dir_alias}}();
		}else{
			$self->{CACHE}->{$dir_alias} = "";  # initialize a string for the cache
			if (-d $self->{DIRS}->{$dir_alias}) {
				# open the directory and then put all the element names on the cache string for lookup separated by a space
				# THIS IS NOT GOOD, WHAT IF A FILENAME HAS A SPACE IN IT!
				opendir(DIR, $self->{DIRS}->{$dir_alias}) || die "can't open " . $self->{DIRS}->{$dir_alias} . ":$!";
				grep {$self->{CACHE}->{$dir_alias} .= $_ . " "} readdir (DIR);
				closedir(DIR);
			} else {
				print "ERROR: Can't cache a single file\n";
				next;
			}
		#}
	}
}
--snip--
So you see, I want to be able to load a special function that refrences the %CACHE property of the object. I want to do something like this:
     # assuming MikeCache has been created
     $mikeCache->LoadSpecialBuilder(ALIAS => \&Func);
You see in the source for the cache object above that I want to just run the special builder function using the $self object in the Build() method with no arguements. Yet, I think that there might be some scoping problems--Like how do I get at the %{$self->{CACHE}} property? Do I have to pass in the $self object? Doesn't this kinda defeat the purpose of object programming? Is there a better way to do what I am trying to do here?

This has been R A D...

Replies are listed 'Best First'.
Re: Re: RE: RE: Scoping and Objects
by chromatic (Archbishop) on Nov 16, 2000 at 04:01 UTC
    If I understand you correctly, you have a cache object. You want to be able to add methods to it at runtime, and some of those methods might need to manipulate the internals of the cache object.

    btrott's technique is good -- that's probably the cleanest way to continue what you're doing. Add some methods to access the cache, and any subroutines added through references don't have to know the particulars about how the cache is implemented.

    Another option (which I do not recommend if there's any other way around it) is to switch to your object's package and define a new subroutine or install one in the package's symbol table:

    package main; # some stuff here package RadCache; sub mangle_cache { # do this and that }
    or
    package main; # stuff here my $sub_ref = sub { print "Messing with the cache.\n" }; *RadCache::mangle_cache = $sub_ref; # syntax may be a little off

    I think you'd be better off just subclassing your cache object whenever you need to add a callback that works on the internals of the object. That may make your design easier to understand, anyway.

Re: Re: RE: RE: Scoping and Objects
by btrott (Parson) on Nov 16, 2000 at 03:52 UTC
    Ah, so you want your callback to have access to your object. Well, that's easy: yes, pass in the $self object:
    $callback->($self);
    This in itself isn't bad OO programming, and it doesn't defeat the purpose of OO. *However*, what if the callback then wants to access the cache? In your current scheme it would have to directly access
    $self->{CACHE}
    That's not good: you don't want some random callback function altering your object's attributes. So what you should do is, your class should provide some object methods to manipulate the cache. For example:
    sub add_to_cache { my $self = shift; my $alias = shift; my @files = shift; push @{ $self->{CACHE}->{$alias} }, @files; }
    To be used in a callback function like this:
    $cache->add_to_cache(ALIAS, @files_in_dir);
    Is this what you had in mind? You no longer have to muck around with your object's CACHE attribute.

    Notice also that, instead of appending to a string, as you do, I'm pushing the directory name onto an array. This will get around your problem of filenames with spaces.