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

perl 5.8.8
(use strict; -wT, use diagnostics)

I have traveled googles of miles to reach your doorstep... I am new to OOP (Damian Conway's book is helping). I use the array of hashes as the query resultset returns multiple rows. Thinking: I want the benefits of arrays so put the hashes in the array rather than only hashes. I bless an array of hashes (the result of a sql query) and test it using

my $itemref = Item::Item->new( "10056" ); my @item = @$itemref; print $item[0]->{_description}, "\n";

works fine. I have a method in the Item::Item file

sub get_desc { $_[0]->{_description}; }

when I try
print $item[0]->get_desc(), "\n";
I get:

Can't call method "get_desc" on unblessed reference at testitem.pl lin +e 17 (#1) (F) A method call must know in what package it's supposed to run. + It ordinarily finds this out from the object reference you supply, b +ut you didn't supply an object reference in this case. A reference i +sn't an object reference until it has been blessed. See perlobj. Uncaught exception from user code: Can't call method "get_desc" on unblessed reference at testite +m.pl line 17. at testitem.pl line 17

My reading suggests a) I need to bless the sub or b) closure. Neither of which I want to do or am familiar with. I know I want to use get_ and set_ methods and believe this is a simple syntax problem on either the sub or my calling of the sub. I have tried in the sub get_desc

sub get_desc { my (@class, $count) = @_; return $class[$count]->{_description}; } At the print statement print @item->get_desc(), "\n"; print $itemref->get_desc(), "\n";

all with no success. Frankly I am guessing... Thank you,

Under sub new I do

sub new { my ($class, @arg) = @_; $class->_incr_count(); my (@item, $ref, @objref); $sth->execute($company, $_[1]); $sth->bind_columns(undef, \$item[0], \$item[1], etc.. while ($ref = $sth->fetchrow_arrayref()) { $objref[$i] = ( { _item => $item[0] || "", _available => $item[1] || "", _brand => $item[2] || "", etc.. } ); $i++; } return bless \@objref, $class; } # End sub new

based on Herkum I have now tried

sub get_desc() { my $self = shift; if (ref $self) { return ${ $self->{"_description"} }; } else { return $self->{_description}; } }

and called it this way

print $itemref->get_desc(), "\n";

gives

Pseudo-hashes are deprecated at /usr/local/www/cgi-pbin/pmpl/item.pl l +ine 119 (#1) (D deprecated) Pseudo-hashes were deprecated in Perl 5.8.0 and th +ey will be removed in Perl 5.10.0, see perl58delta for more details. +You can continue to use the fields pragma. Argument "POLLOCK SUBMARINER BTR 3.2 OZS" isn't numeric in hash elemen +t at /usr/local/www/cgi-pbin/pmpl/item.pl line 119 (#2) (W numeric) The indicated string was fed as an argument to an operator + that expected a numeric value instead. If you're fortunate the mess +age will identify which operator was so unfortunate. Bad index while coercing array into hash at /usr/local/www/cgi-pbin/pm +pl/item.pl line 119 (#3)(F) The index looked up in the hash found as +the 0'th element of a pseudo-hash is not legal. Index values must be + at 1 or greater. See perlref. Uncaught exception from user code: Bad index while coercing array into hash at /usr/local/www/cgi +-pbin/pmpl/item.pl line 119. at /usr/local/www/cgi-pbin/pmpl/item.pl line 119 Item::Item::get_desc('Item::Item=ARRAY(0x8135c88)') called at +testitem.pl line 17

Thank you

Replies are listed 'Best First'.
Re: bless array of hashes correct get method wrong
by Thelonius (Priest) on Apr 20, 2007 at 15:56 UTC
    The first error is complaining that $item[0] is not blessed, which, from the code you posted, is not. You apparently blessed the reference to the whole array, which is not the same as blessing the individual array elements. This is not a pedantic distinction, either. The individual elements are hash references and would normally be in a separate class.

    Actually, it's not clear from your code whether you need the array to be an object, too. Do you have methods that use the array as a whole? It's perfectly fine to have an array of objects.

    Also, this line would never work:

    my (@class, $count) = @_;
    because all the items in @_ will go to @class and $count will be left undefined. That's one reason you pass references to arrays instead of arrays.
Re: bless array of hashes correct get method wrong
by naikonta (Curate) on Apr 20, 2007 at 16:02 UTC
    You mixed things between dereferencing the object reference (my @item = @$itemref;) and calling the object method ($item[0]->get_desc()). The former is just fine for the derefencing sake, but for the latter, you use $item[0] as object to call the method. But $item[0] is not an object, it's just an array element in HASH reference. So, it's true that $item[0] is a reference, but it's unblessed, so you can't use it to call a method, hence the error: Can't call method "get_desc" on unblessed reference.

    You should call it with $itemref, this is the variable holds the object. Even if this was the case, you would get error because in your method definition, you use an array reference but dereference it as HASH.

    The $_[0] is the first element of @_, which holds parameters passed in to methods/subroutines. In a method, $_[0] is supposed to be the object itself, hence the use of $self as a convetional variable name.

    I assume that you (hopefully) have a sort of this structure:

    # simplified version my @array = ({id=>1, name=>'Linux'}, {id=>2, name=>'Perl'}); my $self = [@array]; bless $self, $class

    Remember, the first argument to method is the object itself. Your object is a reference to an ARRAY so you can't dereference it as HASH. Instead, you want something like this.

    sub get_desc { my($self, $idx) = @_; return $self->[0]{_description}; }

    And you call it like this,

    my $item = Item::Item->new('some record id'); print $item->get_desc(0);

    You should stay with your object after creating it, don't dereference to use it to call an object method.

    my $itemref = Item::Item->new( "10056" );
    So, you have the Item::Item object reference in $itemref. The statement,
    @item = @$itemref; print $item[0]->{_description};

    gave you the right thing because you treated it as data structure. But remember, your object is $itemref. So stick with it whenever you want to use it as object.

    When I first learned about OOP (object oriented Perl :-), I found perlobj and perltoot as good introductions and enjoyful to read.


    Udate: added print in relevant snippets

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

      Thanks to all, for all the help (I have been learning perl for a couple of months - all prior work procedural, and like it). I did not realise I would need to bless slices of an array to. I don't want to move the sql statement outside of the package so it calls the bless function six times (correct me if I am wrong) but have it all contained (except the where part of the query) in the package. So I followed your code, changing

      return bless \@objref, $class; to $self = [@objref]; return bless $self, $class; and changed the sub to sub get_desc() { my ($self, $idx) = @_ ; return $self->[$idx]{_description} ; }

      calling code from

      my $item = Item::Item->new( "10056" ); print $item->get_desc(0), "\n";

      it works!. However when I try the next entry (the sql statement returns 6 rows)

      my $item = Item::Item->new( "10056" ); print $item->get_desc(1), "\n";

      I get: Use of uninitialized value in print at testitem.pl line 17. In my while statement I assign each row to the $objref[$i++] slice. The $self = [@objref] should have the whole array reference (handle ? - not sure) and so why does it not work? More reading to do...

      .

        Sorry my mistake - this query only returns 1 row. I tried it with success on a multi-row query. Thank you.

Re: bless array of hashes correct get method wrong
by wfsp (Abbot) on Apr 20, 2007 at 15:57 UTC
    If you have an array of hashes then your getter would need an index (a number) and a key value to get at the the data.

    Perhaps something like this:

    #!/usr/bin/perl use strict; use warnings; { package Item; sub new { my ($class) = @_; my $self = [ {fld1 => 1, fld2 => 2}, {fld1 => 3, fld2 => 4}, ]; bless $self, $class; } sub get_fld { my ($self, $rec, $fld) = @_; return $self->[$rec]{$fld}; } } my $item = Item->new; my $fld = $item->get_fld(1, 'fld1'); print "$fld\n";
    outputs 3
Re: bless array of hashes correct get method wrong
by Herkum (Parson) on Apr 20, 2007 at 15:47 UTC
Re: bless array of hashes correct get method wrong
by logie17 (Friar) on Apr 20, 2007 at 15:39 UTC
    Could you post what your your new method looks like? I'm guessing you're not blessing a reference for that class name. If you post some more code we might be able to help.
    Thanks
    s;;5776?12321=10609$d=9409:12100$xx;;s;(\d*);push @_,$1;eg;map{print chr(sqrt($_))."\n"} @_;
Keep it simpler to start with
by doom (Deacon) on Apr 21, 2007 at 06:00 UTC
    I think you're letting yourself get distracted by all the nifty options that perl allows. If you're just learning about OOP in perl, you should keep things as simple as possible. Stick to using hashref based objects to begin with, which would imply that your aref should be just one field of data inside that object. If it seems helpful, each of the hashrefs inside that array could also be objects of a different class. And while most likely you'll want to just have the usual "get_*" and "set_*" methods that work on that aref as a whole, if you like you can have some "fancy accessors" that do things like push a hash ref onto the array, or pop one off of it.

    (Oh, and one more hint: ignore whatever you hear about "Inside-Out Objects" for a little while longer. There's still room for debate about what the right way is to implement those, and most OOP perl code you see will be hash based).