Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

How can one determine context within FETCH?

by bluebutton (Initiate)
on Oct 01, 2007 at 09:13 UTC ( [id://641857]=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to map a hash over a filesystem so that the following is possible:

$root = $mysystem->root; $proc = $root->{'proc'}; print "Uptime is ", $proc->{'uptime'}[0], "\n";

In other words, $mysystem->root returns a tied hash whose keys are the names of the files and directories in the root directory. However, I want the values to depend on the context. In scalar context:

$passwd = $root->{'etc'}->{'passwd'}; $spool = $root->{'var'}->{'spool'};
the value should be a reference to another tied hash of the same class.

In array context:

$line = $root->{'etc'}->{'passwd'}[0] # First line of passwd file @names = $root->{'var'}->{'spool'}; # List of files/dirs

the value should a tied array representing the entire file as an array of lines (if the key is a file name) or a list of the contents of the directory (if the key is the name of a directory)..

The problem is that using wantarray or looking at the caller() stack, I cannot seem to determine the context within FETCH; it's always scalar.

My question is: Within the FETCH method of a tied hash, is there any other way to distinguish between:

$proc->{'/var'}

and

$proc->{'/var'}[0]

Thanks in advance for any help you can give!

Replies are listed 'Best First'.
Re: How can one determine context within FETCH?
by kyle (Abbot) on Oct 01, 2007 at 12:02 UTC

    Well, no. In both cases, you're fetching a scalar. In the second case, it's a reference to an array. I tried this expression: "($proc->{'/var'})[0]", and it's still getting a scalar, probably because that's all you ever get out of a hash.

    To do what you want to do, you're going to have to explicitly tell the object what you want. Here are some ideas:

    • Have two separate classes, one that returns an array ref, and one that returns scalars.
    • Use some flag in the key that's fetched. For example, "$proc->{'/var'}" is the scalar and "$proc->{'/var/'}" is the array ref. You have to be careful that the flag is not something that could exist in a real filename. All that comes to my mind is "\000" and the slash.
    • An ugly toggle before each call.

    By the way, what you're doing reminds me of Path::Class, though it's not exactly the same thing.

    Update: Another thought for you: ditch the hash interface completely. Then you can say "$proc->get_x('/var')" and "$proc->get_y('/var')" and anything else you want.

Re: How can one determine context within FETCH?
by jasonk (Parson) on Oct 01, 2007 at 12:53 UTC

    Even if you can figure out how to get the behavior you want (which, IMHO, is counter-intuitive) using a tied interface is still going to limit you to the point where you will probably eventually turn it into an object interface anyway. That being said, it sounds like you are reinventing Path::Class, so you might want to take a look at that...

    use Path::Class qw( dir file ); my $root = dir( '/' ); my $spool = $root->subdir( 'var', 'spool' ); my $line = ($spool->slurp)[0]; my @names = $spool->children;

    We're not surrounded, we're in a target-rich environment!
Re: How can one determine context within FETCH?
by jeanluca (Deacon) on Oct 01, 2007 at 12:13 UTC
    I get the impression that you can only give the reference of the tied object and the key to the FATCH method (correct me if I'm wrong). If you give a tied array to your tied hash it will work as well. Here is my code that proves this (I hope it is useful)
    use strict; use warnings; use Tie_hash ; use Tie_array ; my %hash; my @arr ; tie %hash, 'Tie_hash' ; tie @arr, 'Tie_array' ; $hash{'key'} = \@arr ; print $hash{'key'}->[0] ; untie %hash; untie @arr ; package Tie_Array; use strict; use warnings; sub TIEARRAY { my $obj = [ "hi" ] ; return bless $obj, shift ; } sub FETCH { my ( $self, $key) = @_ ; return $self->[$key] ; } sub STORE {} sub DESTROY {} package Tie_hash; use strict; use warnings; sub TIEHASH { return bless {}, shift ; } sub FETCH { my ( $self, $key) = @_ ; return $self->{$key} ; } sub STORE { my ( $self, $key, $val ) = @_ ; $self->{$key} = $val ; } sub DESTROY { }

    Cheers
    LuCa
Re: How can one determine context within FETCH?
by Sidhekin (Priest) on Oct 01, 2007 at 13:15 UTC

    You can probably use overload to overload the use of your objects(!) as arrayrefs and hashrefs. See the cookbook section on "Two-face references" for an example.

    However, your (whole) array context will need to look like this:

    @names = @{$root->{'var'}->{'spool'}}; # List of files/dirs

    (Which to my mind is a plus; having a hash element lookup return a list is at best just too surprising and at worst downright abuse of syntax. YMMV.)

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

Re: How can one determine context within FETCH?
by Anno (Deacon) on Oct 01, 2007 at 12:20 UTC
    The module Want looks like it might offer a workaround, but unfortunately it says:
    BUGS
    * Doesn't work from inside a tie-handler.
    Anno
Re: How can one determine context within FETCH?
by clinton (Priest) on Oct 01, 2007 at 13:13 UTC
    Just to make something clear, by trying to assess context in your tied hash, you are breaking a Perl idiom: that hash values are only ever scalars. That scalar may be a reference to something else, like an array or hash, but the hash value itself is only ever a scalar.

    Consider this:

    %fs = ( dir1 => [ 'file1','file2','file3' ], dir2 => [ 'file4','file5','file6' ], ); $contents = $fs{dir1}; $file = $fd{dir1}[0];

    I would expect $contents to contain an array-ref, and $file to contain a scalar string. If I wanted an array returned instead, I would write:

    @contents = @{$fs{dir1}};
    which would still work with your code, because the @ converts the reference to a list, OUTSIDE the tied class. To do otherwise would, in my opinion, just add confusion: this works just like a hash, except when it doesn't...

    Clint

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2024-04-19 12:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found