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

Trying to get my head around moose and running into a problem. Cut down version of code
package AXRecord; # Our libraries use lib 'C:\Users\Jay\Desktop\SBS DEV\CODE\perl\Utilities'; use AXControl; use AXSQL; use Moose; use DBI; # Attributes has 'Keys' => (is => 'rw', isa=>'ArrayRef'); # Contains a single record sub BUILD # Constructor { } sub Select { #Get the primary key fields $self->Keys (()); $sql = AXSQL->new(ControlObject => $self->ControlObject, SQLString + => "SELECT column_name FROM information_schema.`key_column_usage` WH +ERE table_name = '" . $self->Name . "' order by ordinal_position"); $i = 1; while (($Col) = $sql->Fetch()) { $self->Keys()->[$i] = $Col; $i++; } $Cnt = scalar $self->Fields; $self->FieldCount = $Cnt; $self->Changed = -1; } 1;
Problem is in the select method where I try to build the keys array. Specifically where I'm adding elements with the statements
$self->Keys()->[$i] = $Col;
The error message I'm getting is
Can't use an undefined value as an ARRAY reference at C:\Users\Jay\Des +ktop\SBS DEV\CODE\perl\Utilities/AXRecord.pm line 106. C:\Users\Jay\Desktop\SBS DEV\CODE\perl>

Replies are listed 'Best First'.
Re: Moose arrayref addition: noob
by choroba (Cardinal) on Oct 20, 2017 at 21:42 UTC
    You can initialize the attribute with a default value of an empty array. You can then assign to a given index, or you can push to the array with dereference:
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package My::Object; use Moose; has keys => ( is => 'ro', isa => 'ArrayRef', default => sub { [] } ); __PACKAGE__->meta->make_immutable; } my $o = 'My::Object'->new; $o->keys->[0] = 'def'; push @{ $o->keys }, 'abc'; say for @{ $o->keys };

    Another possibility is to use traits:

    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package My::Object; use Moose; has keys => ( is => 'ro', traits => ['Array'], handles => { add_keys => 'push', get_keys => 'elements' } ); __PACKAGE__->meta->make_immutable; } my $o = 'My::Object'->new; $o->add_keys('abc', 'def'); say for $o->get_keys;

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Moose arrayref addition: noob
by kcott (Archbishop) on Oct 21, 2017 at 03:38 UTC

    G'day jorba,

    Either the code you posted is not what you ran or you haven't shown us the complete output.

    I created "pm_1201774_moose_demo.pm", which I'm going to modify in stages, as I show the output when testing, and make fixes.

    To start with, here's a very minimal version to show an error you should have seen:

    package Demo; use Moose; has test => (is => 'rw', isa => 'ArrayRef'); sub check { $self->test(()); }

    As Moose automatically turns on the strict pragma:

    $ perl -c pm_1201774_moose_demo.pm Global symbol "$self" requires explicit package name (did you forget t +o declare "my $self"?) at pm_1201774_moose_demo.pm line 8. pm_1201774_moose_demo.pm had compilation errors.

    Changing &check to declare $self:

    sub check { my ($self) = @_; $self->test(()); }

    That problem is now fixed:

    $ perl -c pm_1201774_moose_demo.pm pm_1201774_moose_demo.pm syntax OK

    Now I add code to emulate your error.

    package Demo; use Moose; has test => (is => 'rw', isa => 'ArrayRef'); sub check { my ($self) = @_; $self->test(()); $self->test()->[0] = 'X'; } package main; Demo::->new->check;

    Note the syntax is still fine; your error occurs at runtime.

    $ perl -c pm_1201774_moose_demo.pm pm_1201774_moose_demo.pm syntax OK $ perl pm_1201774_moose_demo.pm Can't use an undefined value as an ARRAY reference at pm_1201774_moose +_demo.pm line 12.

    In "$self->test(());", your argument is an empty list which will flatten to nothing; i.e. it's the same as "$self->test();". This is an accessor; I believe you want a mutator (I'm guessing this would be to reinitialise that attribute).

    In order for that method to act as a mutator, you need to pass a value of an appropriate type (in this case: isa => 'ArrayRef'), such as:

    $self->test([]);

    Now the syntax is still OK and the runtime error no longer occurs.

    $ perl -c pm_1201774_moose_demo.pm pm_1201774_moose_demo.pm syntax OK $ perl pm_1201774_moose_demo.pm $

    Compare that with your problem two days ago:

    ... Fields ... isa => 'HashRef' ... $self->Fields({});

    And, if that Fields() method is intended to be the same as the one in your current code:

    $Cnt = scalar $self->Fields;

    I suspect you're going to have a problem with that also. Here's a hint:

    $ perl -E 'my $x = {a=>1,b=>2}; say scalar $x; say scalar keys %$x' HASH(0x7ffe120040b0) 2

    And looking at the two lines after that:

    $self->FieldCount = $Cnt; $self->Changed = -1;

    You're going to get the same errors that you had in your last question. Frankly, they seem unrelated to your current issue and I don't why you posted them; however, if these haven't changed:

    ... FieldCount ... isa => 'Num' ... ... Changed ... isa => 'Boolean' ...

    You'll end up with repeats of:

    ... Can't modify non-lvalue subroutine call at ...

    Furthermore, using "-1" as a boolean value is likely to be highly confusing (and, therefore, error-prone). It's also probably just wrong, as a "SELECT" statement is a read-only operation. I'd expect the value of "Changed" to be false, but:

    $ perl -E 'my $x = -1; say $x ? "true" : "false"' true

    — Ken