For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > April 2005 > basic class problem









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author basic class problem
Graeme McLaren

2005-04-24, 3:55 pm

Hi all I'm trying to write a basic class that will contain some
configuration details, unfortunately it bombs out and doesn't do anything.

################### start of class ###########################
#configuration.pm
package configuration;
use strict;


sub new{
my $class=shift;
my $self=shift;

bless($self, $class);
return $self;
}

sub get_conf{
my $self=shift;
my %conf=(
db_name="bla1",
username="bla2",
password="bla3"

);

return \%conf;

}

1;
################ end of class ####################


Mt CGI script is this:

##################### CGI Script #######################
#!/usr/bin/perl -w
use strict;
use configuration;

print "Content-type: text/html\n\n";

my $conf=configuration->new;
my $conf->get_conf;

print "$conf->{'db_name'}";
########################################
###


All I want to do is print off the value of the hash refs at the moment
before I start building on it. I know I'm not passing anything to the "new"
constructor but I'm not sure what I should be passing to it. The name of
the class shouldn't be defined outside the class surely?

Anyone have any ideas?


Cheers,

Graeme :)


Lawrence Statton

2005-04-24, 8:55 pm

> Hi all I'm trying to write a basic class that will contain some
> configuration details, unfortunately it bombs out and doesn't do anything.
>
> ################### start of class ###########################
> #configuration.pm
> package configuration;
> use strict;
>
>
> sub new{
> my $class=shift;
> my $self=shift;



And what is getting shifted into $self? You only call the constructor with one (implicit) parameter - the class-name

$configuration = configuration->new;

is (nearly) equivalent to

$configuration = configuration::new('configuration');




>
> bless($self, $class);
> return $self;
> }


A better constructor might be (assuming you're going to represent your
object as a hash-reference, which is the most popular, but not only
way to do it. (read and reread perltoot until you understand all of it).

sub new {
my $class = shift;
bless ({}, $class) ;
return $self;
}


>
> sub get_conf{
> my $self=shift;
> my %conf=(
> db_name="bla1",
> username="bla2",
> password="bla3"
>
> );


Well, the problems here are manifold:

1) The syntax for generating a hash is

my %conf = ( db_name => 'bla1',
username => 'bla2',
password => 'bla3' ) ;


2) You create and return a hashref without making any change to the
object; Maybe what you meant to do was something more like:

sub get_conf {
my $self = shift;
my %conf = ( db_name => 'bla1',
username => 'bla2',
password => 'bla3' ) ;

foreach my $k ( keys %conf ) {
$self->{$k} = $conf{$k};
}

return $self;
}


> Mt CGI script is this:
>
> ##################### CGI Script #######################
> #!/usr/bin/perl -w
> use strict;
> use configuration;
>
> print "Content-type: text/html\n\n";
>
> my $conf=configuration->new;
> my $conf->get_conf;



>
> print "$conf->{'db_name'}";


This is a subtler but much more dreadful problem:

ISO8859-1 doesn't provide a bloody skull-and-crossbones character --
but this should be set in animated red flashing bell-ringing type.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT DIRECTLY ACCESS THE UNDERLYING STRUCTURE OF YOUR OBJECTS !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

If you have to, print that out and paste it to your monitor.

Provide accessor and mutator functions (a/m) for the member data.

sub db_name {
my $self = shift;
if (@_) {
$self->{db_name} = shift;
return $self;
} else {
return $self->{db_name};
}
}

This does lead to a philosophical discussion: What should the mutator
return?

There are pretty much three values that make sense:

1) the now new value of db_name
2) $self
3) The previous value of db_name

Number one is a tautology -- it is the least useful -- you KNOW what
you just set the value to, why bother returning it.

Number two is almost a tautology - but it allows you to use
constructions such as:

$self->db_name('dbi:pg:dbname=data')->username('webserver')->password('secret');

I use this in all of my base classes.

If I didn't return $self, I would choose number three -- as it is the
only of the three options that gives you some piece of data you didn't
pass INTO the function, and is sometimes useful.

rewriting my a/m function would look like:

sub db_name {
my $self = shift;
if (@_) {
my $prev = $self->{db_name};
$self->{db_name} = shift;
return $prev;
} else {
return $self->{db_name};
}
}

This allows you to write code like:

my $old_db = $self->db_name('dbi:pg:dbname=tempdata');

$self->do_something(); # with the temporary database

$self->db_nmae($old_db); # go back to the original database


Now -- to get back to my first warning:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT DIRECTLY ACCESS THE UNDERLYING STRUCTURE OF YOUR OBJECTS !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

"WHY!!?", I hear you cry.

Because today, you're just storing the db_name in the object. But
next month, you need to make a change:

Boss: Now we need to automatically change the underlying database
based on which customer number is stored in the object, because each
configuration comes out of a different database.

Now, we have two possible scenarios:

Stupid-self: Oh, shit. I hadn't thought of that. I have to search
for EVERY place I *USED* the configuration object, and make a change.

Smart-self: No problem. I just change the semantics in
configuration.pm and every place that used the db_name method will
automatically Do The Right Thing.

sub db_name {
my $self = shift;

# I will pull the customer_number out so I can use
# it in a string interpolation

my $customer_number = $self->customer_number;

my $db_name = " dbi:pg:dbname=cust${customer_number}data
";
return $db_name;
}

So, in summary

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT DIRECTLY ACCESS THE UNDERLYING STRUCTURE OF YOUR OBJECTS !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

The only place you should ever see something of the form

$self->{foo}

is *INSIDE* the defining class.

And even there, you *USUALLY* should not. Notice in my little
example, I used $self->customer_number.

Even though I was "inside" the class for configuration, and I "know"
that customer_number is an attribute of the object, it is Better to
avoid using my insider knowledge unless there is a Very Good Reason.

Places that include Very Good Reason is handling complex
polymorphism; writing generic a/m methods; questions of caching of
high-cost computations.

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Lawrence Statton - lawrenabae@abaluon.abaom s/aba/c/g
Computer software consists of only two components: ones and
zeros, in roughly equal proportions. All that is required is to
sort them into the correct order.


Charles K. Clarkson

2005-04-24, 8:55 pm

lawrence@cluon.com <mailto:lawrence@cluon.com> wrote:

: A better constructor might be (assuming you're going to represent
: your object as a hash-reference, which is the most popular, but
: not only way to do it. (read and reread perltoot until you
: understand all of it).
:
: sub new {
: my $class = shift;
: bless ({}, $class) ;
: return $self;
: }

Is $self a file scoped variable or is this a typo? Perhaps
you meant this?

sub new {
my $class = shift;
return bless {}, $class;
}


HTH,

Charles K. Clarkson
--
Mobile Homes Specialist
254 968-8328

John Doe

2005-04-25, 8:56 am

Hi Graeme

[rearranged from bottom:]
> All I want to do is print off the value of the hash refs at the moment
> before I start building on it.


Due to my bad english I'm not quite shure what you mean by that.
You want to print the values to be sure they are right, before using them?

> I know I'm not passing anything to the
> "new" constructor but I'm not sure what I should be passing to it.
> The
> name of the class shouldn't be defined outside the class surely?


The name of the class (a package name) is implicitly passed with

my $obj = My::Class->new(); # Class is named My::Class

This is catched in the new() code by

my $class=shift;

Similarily, the object is implicitly passed when calling a method:

my $result=$obj->method();

This is catched in the new() code by

my $self=shift;


Start with

perldoc perl

from cmdline to study some documentation.


Am Sonntag, 24. April 2005 19.01 schrieb Graeme McLaren:
> Hi all I'm trying to write a basic


I suppose you mean "easy, static class" here, not "base class", for my
comments.

> class that will contain some
> configuration details, unfortunately it bombs out and doesn't do anything.
>
> ################### start of class ###########################
> #configuration.pm
> package configuration;
> use strict;


add:

use warnings;

which may give some help if anything doesn't behave as you expect.

> sub new{
> my $class=shift;
> my $self=shift;
>
> bless($self, $class);
> return $self;
> }


The sense of a new() method is to create an object ($self), so it is not
passed to new().

Additionaly, since new() already returns the created object, you dont need a
methode "get_conf" with the sematic of "get the configuration object".

Instead, you could use get_conf() to get a specific configuration value.

> sub get_conf{
> my $self=shift;
> my %conf=(
> db_name="bla1",
> username="bla2",
> password="bla3"
>
> );
>
> return \%conf;
>
> }
>
> 1;



So, I would rewrite new() and get_conf() as follows:
[untested]

# create configuration object:
#
sub new {
my $class=shift;
# create the object to return, the object containing
# the configuration variables and their values:
my $self={
name1=>value2,
name2=>value2,
}
# return the object, consisting of reference to a hash,
# blessed to your class:
return bless $self, $class;
}

# get some configuration value
#
sub get_conf {
my $self=shift;
my $var=shift;
die "unknown config var"
unless exists $self->{$var};
return $self->{$var};
}


> ################ end of class ####################
>
>
> Mt CGI script is this:
>
> ##################### CGI Script #######################
> #!/usr/bin/perl -w
> use strict;
> use configuration;
>
> print "Content-type: text/html\n\n";
>
> my $conf=configuration->new;
> my $conf->get_conf;
>
> print "$conf->{'db_name'}";


According to my code above, the script gets adapted:

my $conf=configuration->new; # as you wrote
print $conf->get_conf('name1');

> ########################################
###
>
> Anyone have any ideas?



BUT:

If your configuration has not to be generic, but contain only some fixed
values for your web interface, and you use the configuration only in one
script, there is no need to use a class!

Simply use a hash.

So, your script could simply be (without a configuration package):

#!/usr/bin/perl -w
use strict;
use warnings;

print "Content-type: text/html\r\n\r\n";

my %conf=(bla=>'blubb', foo=>'bar');

# print the configuration
print join "<br />", map {$_. ": ".$conf{$_}} keys %conf;

# and now use the config values in the script code by
# accessing hash keys, eg:
# do_something_with ($conf{bla});


hth, joe
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2009 codecomments.com