CDaly
CDaly

Reputation: 67

How to return the correct object attribute in perl?

I have a package I created that is meant to return the object's specified attribute (shortened code)

package vendor_info;

my $vars;
sub new{
    my $class = shift;
    $vars = {
        _servers => shift,
        _locations => shift,
        _favorite => shift,
        _default_selection => shift,
        _database => shift,
        _DB => shift};

    bless $vars, $class;
    return $vars;
}
sub get_locations{
    return $vars->{_locations};
}

sub get_database{
    return $vars->{_database};
}
sub get_DB{
    return $vars->{_DB};
}

My perl file receives an input parsed from the terminal but in this case, the variable $vendor is hard coded for testing. I have a list of objects in a hash, and I want to return the correct attribute according to the object. Some of the variables have been removed with placeholders.

$vendor = "atrena";

my %vendor_hash = (
    "atrena" => new vendor_info("Variable_server","Variable_location","Advanced_CDC|CDC dftso|DFT|gui|GUI|adv_checker|Lint|spl-view-only|view-only-GUI","adv_checker","database","DB"),

    "ansys" => new vendor_info("Variable","Location","agppi|agppi|ane3fl|ane3fl|ansys|ansys|ensemble_gui|ensemble_gui|hfss_desktop|hfss_desktop|hfss_gui|hfss_gui|hfss_solve|hfss_solve|hfsshpc_pack|hfsshpc_pack|optimetrics|optimetrics|q3d_desktop|q3d_desktop|rdacis|rdacis|struct|struct","ane3fl","database", "db"),

    "coventor" => new vendor_info("var","location","COV_ZsplatViewer|Viewer|COV_VoxelModeler|Voxel-Modeler|MEMSp_Import_Package|Import-Package|MEMSp_Innovator_Plugin|Innovator-Plugin|MEMSp_MATLAB_Simulation|MATLAB-Simulation|MEMSp_Platform|Platform|MTI_AutoBuilder|Auto-Builder|MTI_Catapult|Catapult|MTI_CoventorWare|Coventor-Ware|MTI_Memcap|Memcap|MTI_PreProcessor|PreProcessor","database","db","db")   

);

$vendor_object = $vendor_hash{$vendor};

print Dumper( $vendor_object);

$foodb = $vendor_object -> get_database();

The dumper is printing the correct information, however, when I call get_database(), the database called is always the attribute from the last object in the list, which in this case is coventor. The same could be said for any of the sub routine getters.

How do I call the correct attribute for the correct object?

Upvotes: 3

Views: 121

Answers (3)

Borodin
Borodin

Reputation: 126722

In addition to other people's points, you will need to add a true statement at the end of your .pm file

This is how I would write your application

VendorInfo.pm

package VendorInfo;

use strict;
use warnings 'all';

sub new {
    my $class = shift;

    my $self;

    @{$self}{qw/
        _servers
        _locations
        _favorite
        _default_selection
        _database _DB
    /} = @_;

    return bless $self, $class;
}

sub get_locations {
    my $self = shift;
    return $self->{_locations};
}

sub get_database {
    my $self = shift;
    return $self->{_database};
}

sub get_DB {
    my $self = shift;
    return $self->{_DB};
}

1;

main.pl

use strict;
use warnings 'all';

use VendorInfo;
use Data::Dumper;

my $vendor = 'atrena';

my %vendor_hash = (
    atrena => VendorInfo->new(
        'Variable_server',
        'Variable_location',
        'Advanced_CDC|CDC dftso|DFT|gui|GUI|adv_checker|Lint|spl-view-only|view-only-GUI',
        'adv_checker',
        'database',
        'DB',
    ),
    ansys => VendorInfo->new(
        'Variable',
        'Location',
        'agppi|agppi|ane3fl|ane3fl|ansys|ansys|ensemble_gui|ensemble_gui|hfss_desktop|hfss_desktop|hfss_gui|hfss_gui|hfss_solve|hfss_solve|hfsshpc_pack|hfsshpc_pack|optimetrics|optimetrics|q3d_desktop|q3d_desktop|rdacis|rdacis|struct|struct',
        'ane3fl',
        'database',
        'db',
    ),
    coventor => VendorInfo->new(
        'var',
        'location',
        'COV_ZsplatViewer|Viewer|COV_VoxelModeler|Voxel-Modeler|MEMSp_Import_Package|Import-Package|MEMSp_Innovator_Plugin|Innovator-Plugin|MEMSp_MATLAB_Simulation|MATLAB-Simulation|MEMSp_Platform|Platform|MTI_AutoBuilder|Auto-Builder|MTI_Catapult|Catapult|MTI_CoventorWare|Coventor-Ware|MTI_Memcap|Memcap|MTI_PreProcessor|PreProcessor',
        'database',
        'db',
        'db',
    ),
);

my $vendor_object = $vendor_hash{$vendor};

print Dumper $vendor_object;

my $foodb = $vendor_object->get_database;

print $foodb, "\n";

output

$VAR1 = bless( {
                 '_servers' => 'Variable_server',
                 '_default_selection' => 'adv_checker',
                 '_locations' => 'Variable_location',
                 '_database' => 'database',
                 '_DB' => 'DB',
                 '_favorite' => 'Advanced_CDC|CDC dftso|DFT|gui|GUI|adv_checker|Lint|spl-view-only|view-only-GUI'
               }, 'VendorInfo' );
database

Upvotes: 4

melpomene
melpomene

Reputation: 85757

Dave Cross already answered your immediate question.

This is an example of a more idiomatic version of your code:

{
    package VendorInfo;
    use Moo;

    for my $attr (qw(
        servers
        locations
        favorite
        default_selection
        database
        DB
    )) {
        has $attr => (
            is       => 'ro',
            required => 1,
        );
    }
}


# main program
use strict;
use warnings;

use Data::Dumper;

my %vendor_hash = (
    "atrena" => VendorInfo->new(
        servers           => "Variable_server",
        locations         => "Variable_location",
        favorite          => "Advanced_CDC|CDC dftso|DFT|gui|GUI|adv_checker|Lint|spl-view-only|view-only-GUI",
        default_selection => "adv_checker",
        database          => "database",
        DB                => "DB",
    ),

    "ansys" => VendorInfo->new(
        servers           => "Variable",
        locations         => "Location",
        favorite          => "agppi|agppi|ane3fl|ane3fl|ansys|ansys|ensemble_gui|ensemble_gui|hfss_desktop|hfss_desktop|hfss_gui|hfss_gui|hfss_solve|hfss_solve|hfsshpc_pack|hfsshpc_pack|optimetrics|optimetrics|q3d_desktop|q3d_desktop|rdacis|rdacis|struct|struct",
        default_selection => "ane3fl",
        database          => "database",
        DB                => "db",
    ),

    "coventor" => VendorInfo->new(
        servers           => "var",
        locations         => "location",
        favorite          => "COV_ZsplatViewer|Viewer|COV_VoxelModeler|Voxel-Modeler|MEMSp_Import_Package|Import-Package|MEMSp_Innovator_Plugin|Innovator-Plugin|MEMSp_MATLAB_Simulation|MATLAB-Simulation|MEMSp_Platform|Platform|MTI_AutoBuilder|Auto-Builder|MTI_Catapult|Catapult|MTI_CoventorWare|Coventor-Ware|MTI_Memcap|Memcap|MTI_PreProcessor|PreProcessor",
        default_selection => "database",
        database          => "db",
        DB                => "db",
    ),
);

my $vendor = "atrena";

my $vendor_object = $vendor_hash{$vendor};

print Dumper($vendor_object);

print "The database is: ", $vendor_object->database, "\n";

Things of note:

  • I renamed vendor_info to VendorInfo. Lowercase module names are (informally) reserved for pragmata.
  • I used Moo as a helper module for writing classes.
  • Moo provides a has helper function for declaring attributes. It also generates a constructor for me, so I don't have to write any boilerplate myself.
  • Moo automatically enables warnings/strict, so I don't have to do that either.
  • Indirect object syntax (method $object or method class, in your case new vendor_info) is a bad idea because of its syntactic ambiguity. class->method (here: VendorInfo->new) is much better.
  • The constructor created by Moo takes its arguments in the form of key-value pairs, not a long list (which is a good idea anyway if your sub takes more than 3 arguments).
  • Every attribute I declared gets a (read-only (because I used 'ro')) accessor, so client code can simply use $object->database.

Upvotes: 4

Dave Cross
Dave Cross

Reputation: 69224

You have $vars as a lexical variable which is scoped to the file which contains your package. So there is only one instance of this variable and it will always contain the data for the last object that was set up.

I'm not sure where you picked up that approach, but it's not how Perl objects work at all. $vars should be scoped to only exist within your constructor and your accessors should be using the object that is passed to them as their first argument (traditionally called $self).

# Only pragmas should start with lower-case letters
package VendorInfo;

sub new{
    my $class = shift;
    my $vars = {
        _servers => shift,
        _locations => shift,
        _favorite => shift,
        _default_selection => shift,
        _database => shift,
        _DB => shift
    };

    return bless $vars, $class;
}

# Just one example accessor...
sub get_database{
    my $self = shift;
    return $self->{_database};
}

One more point, please use Class->new() instead of the potentially problematic new Class syntax that you are using in your code.

Upvotes: 7

Related Questions