Thomas
Thomas

Reputation: 79

Make a class on Perl (class::Std)

I'm asking for your help on Perl stuff.

I currently have my code working well by creating a new object and then using it to run a function, but I would like to use more recent ways of coding with Perl (using class:Std and integrated BUILD/START functions)

So right now my code looks like that, I call new with a path to a log file and then run one function:

# Called from another perl script
use MyClass;
my $Obj = MyClass->new('/usr/me/path.log');
$Obj->run();

# My module
package MyClass;

sub new {
    shift if ( defined($_[0] eq 'MyClass') );
    my %args  = validate( @_,{ LogPath => { type => SCALAR, optional => 0 }, } );

    my $self;
    $self->{plistPath} = $_[1];
    return bless $self;
}

sub run {
    my $self = shift;
    ...
    ...
}

And what I would like to have is a thing like that:

use Class::Std

sub BUILD {
    my $self = shift;
    my $ident = shift;

    my %args  = validate( @_, { LogPath => { type => SCALAR, optional => 0 }, } );

    print "My log path:".$args{LogPath}."\n";

    $self->{logPath} = $args{LogPath};

    return bless $self;
}

sub run {
    my $self = shift;

    print $self->{logPath};

    ....
}

But this doesn't work, it prints well the log path into the BUILD (I just wanted to check here if it works), but I can't get it to register the path into $self->{logPath} to use it in my other functions. It tells me that it is not a Hash reference.

From the tutorials I've done, I think BUILD shouldn't return the $self as it is created automatically with Class::Std, but I don't know how to do.

Your help would be greatly appreciated, if you have any advice.

Thanks a lot, Tim.

Upvotes: 1

Views: 424

Answers (1)

xxfelixxx
xxfelixxx

Reputation: 6602

Since you are learning the more modern style, might I recommend that you give Moose or Moo a try, as they attempt to simplify the object-oriented perl experience, as we as making it much more powerful. These modules have been introduced after Class::Std came about, and represent a more modern approach to creating object. The differences between Moose / Moo (and some others, like Mo or M (joke)) is that Moose gives you the full power of Object Meta Programming, while Moo strives for less power, but more speed.

Here is an example of your code, using Moo:

test.pl

#!/usr/bin/env perl

use strict;
use warnings;

use MyClass;
my $obj = MyClass->new(LogPath => '/var/log/messages');
$obj->run();
1;

MyClass.pm

package MyClass;
use Moo; # Moose / Mouse / Moo

has LogPath => (
    is => 'ro',    # Read Only
    required => 1, # Required
    isa => sub {
        my ($path) = @_;
        die "LogPath must be a SCALAR" if (ref $path);
        die "LogPath [$path] must be a real path" unless -f $path;
    },
);

sub run {
    my ($self) = @_;
    print "MyClass::run()\n";
    my $path = $self->LogPath();
    print "About to do something with LogPath [ $path ]\n";
}

1;

Output

perl test.pl
MyClass::run()
About to do something with LogPath [ /var/log/messages ]

As requested, for comparison, here is the same object, but this time created using Class::Std. Please note how much more code/boilerplate is required with the Class::Std solution vs Moo:

test_class_std.pl

#!/usr/bin/env perl

use strict;
use warnings;

use MyClassStd;
my $obj = MyClassStd->new({ LogPath => '/var/log/messages' });
$obj->run();
print $obj->get_description() . "\n";

MyClassStd.pm

package MyClassStd;

use Class::Std;

# Create storage for object attributes...
# The syntax '%foo : ATTR' applies the attribute named ATTR to the hash %foo.
# For more info on how this works, see: perldoc attributes

# Create one hash per attribute, these will be private to the class
my %log_paths   : ATTR;

# These fields will be available via $obj->get_foo() methods
my %public_data : ATTR;

# Handle initialization of objects of this class...
sub BUILD {
    my ($self, $object_id, $args) = @_;
    my $path = check_path( $args->{ LogPath } );
    $log_paths{ $object_id } = $path;
    $public_data{ $object_id }{ description } = "LogPath is set to [ $path ]";
}

# Handle cleanup of objects of this class...
sub DEMOLISH {
    my ($self, $object_id) = @_;
    # Objects will be removed from the hashes automatically
    # Add any other cleanup code here
}

# Handle unknown method calls...
sub AUTOMETHOD {
    my ($self, $object_id, @args) = @_;

    my $method_name = $_; # Method name passed in $_
    # Return any public data...
    if ( $method_name =~ m/^get_(.*)/ ) {
        my $get_what = $1;
        return sub {
            return $public_data{$object_id}{$get_what};
        }
    }

    warn "Can't call $method_name on ", ref $self, " object";

    return; # The call is declined by not returning a sub ref
}

sub run {
    my ($self) = @_;

    print "MyClassStd::run()\n";
    my ($path) = $log_paths{ ident $self };
    print "About to do something with LogPath [ $path ]\n";

}

sub check_path {
    my ($path) = @_;

    die "LogPath must be a SCALAR" if ( ref $path );
    die "LogPath [ $path] must be a real path" unless -f $path;

    return $path;
}

1;

Output

MyClassStd::run()
About to do something with LogPath [ /var/log/messages ]
LogPath is set to [ /var/log/messages ]

Upvotes: 5

Related Questions