How do I override autogenerated accessors in Perl's Class::DBI? - perl

I followed the example at http://wiki.class-dbi.com/wiki/Overriding_autogenerated_accessors
I want to modify the URL before it is inserted to the database:
package Hosting::Company;
use base 'Class::DBI';
my $class = __PACKAGE__;
$class->table('Companies');
$class->columns(Primary => 'CompanyId');
$class->columns(Others => qw/Name Url Comment/);
sub Url {
my $self = shift;
# modify URL.
if (#_) {
$_[0] = 'aaaaaaaaaaaa';
# return $self->_Url_accessor('aaaaaaaaaaaa'); - doesn't work either
}
# Back to normal Class::DBI
return $self->_Url_accessor(#_);
}
But it doesn't work:
my $company = Hosting::Company->insert({ Name => 'Test', Url => 'http://http://url' });
print $company->Url, "\n";
Shows:
http://http://url
I wish the Class:DBI mailing list were still alive!

In you URL accessor, you check whether a parameter was passed to that method. But you aren't passing anyhting in so the accessor will do nothing but call _Url_accessor(). You should probably call _Url_accessor first and then modify the result:
sub Url {
my $self = shift;
# Was there a param passed in?
if ( #_ ) {
# Do you really want to modify it here?
return $self->_Url_accessor(#_);
}
else {
my $url = $self->_Url_accessor();
# mangle result here:
$url = 'aaaaaaaaa';
return $url;
}
}
If you want to change the URL before it even goes in the database, I guess you must provide a normalize_column_values in your class and this will be called each time an insert is made.

Overriding an accessor does not change insert. The best way to handle data normalization is to override normalize_column_values(). But Manni is right, your accessor is busted.
PS The CDBI mailing list is still active, just hasn't seen much posting. Most have moved on to DBIx::Class.

Related

Can't call method 'isBinary' on an undefined value

I am trying to make a tinier, more optimized CGI module for Perl.
When working on the init subroutine to get, or set default file data, I run into an issue primarily caused with the MIME::Types module.
In reality, my init subroutine is supposed to get the blessed variables from the new constructor, initialize MIME::Types, use MIME::Types to return the MIME::Type of $type via mimeTypeOf(), determine if the MIME::Type is binary, and print out the Content-Type and Content-Disposition.
However, when trying to do something of that nature, I get the title as the error.
This is my current code, which in theory should work.
sub new {
my ($class, %args) = #_;
my $self = {};
my $type = $args{type} // 'text/html'; # If type isn't defined, default to HTML content.
my $attachment = ($args{attachment} // 'false') eq 'true' ? 'attachment' : 'inline'; # Same, but with disposition instead.
$self->{type} = $type;
$self->{attachment} = $attachment;
bless $self, $class;
return $self;
}
sub init {
my $self = shift;
CORE::state $type = shift // $self->{type}; # If there are no overrides, just use $self->type;
# print $self->type; prints text/html when not specified.
CORE::state $attachment = shift // $self->{attachment}; # Same as above, but with $self->attachment;
# print $self->attachment; prints inline when not specified.
my $types = MIME::Types->new;
my $mime = $types->mimeTypeOf($type);
if ($mime->isBinary) {
$self->{attachment} = 'attachment';
} else {
$self->{attachment} = ('inline' or $attachment);
}
die "Warning: Binary content types should not be sent inline!\r\n" if ($mime->isBinary && $attachment eq 'inline');
print "Content-Type: $mime\r\nContent-Disposition: $self->{attachment}\r\n\r\n";
return;
}
Even printing the types return a non undefined value, in fact, it prints out what it's supposed to when there's nothing inside of the new method. This is the code which is in my main CGI file.
#!C:\Strawberry\perl\bin\perl.exe
use strict;
use warnings;
use TinyCGI::Core;
# Create a new TinyCGI::Core object
my $cgi = TinyCGI::Core->new();
# Initialize the TinyCGI::Core object
$cgi->init();
print 'Hello, World!';
This worked perfectly fine without MIME::Types, and even works just fine with stuff actually defined within the object.
I've also tried not using CORE::state.
You are using MIME::Types incorrectly. It looks like you want to take the value from an HTTP header (or maybe multi-part body header) and figure out what it is. You are using mimeTypeOf, which expects a file extension or filename, when you should be using type, which expects a type string.
Either way, there's a chance that you could get back an undef value because MIME::Types might not be able to map the string to a type. You should guard against that:
my $type = $mime->type( $self->{type} );
if( defined $type ) { ... }
Unrelated to that, you are using CORE::state for some reason. That makes it look like you are defining some method or subroutine named state and expecting it to conflict with the Perl built-in.
But I don't think you want state here. You have an instance method init that is for some reason persisting a value based on an earlier created, unrelated instance. I think you want my, since you are merely giving a local name to something that's already inside the invoking instance. You don't want to persist that to the next instance that calls this.

How to call method within builder

I have a class with an attribute set up as follows:
has _data => (
is => 'ro',
lazy => 1,
builder => '_load',
);
sub _load {
my $self = shift;
return retrieve $self->_file;
}
However I now want to call a method already defined on the class before returning the data.
In old-school Perl OO, I'd be doing something like this:
sub _load {
# Assuming laziness is implemented somewhere else.
my $self = shift;
$self->{_data} = retrieve $self->_file;
$self->refresh; # which does something with $self->{_data}
return $self->{_data};
}
But I can't figure out a 'clean' way to do this in Moose.
I've considered the following, but think they are quite ugly, and that there must be a better way of doing this.
If I make _data read-write, I could potentially write the data to the accessor, call the method then return the value from the accessor for Moose to write back to the accessor.
If I turn it into a plain old method then I'd have to define another attribute, say _raw_data, store the data in there, modify refresh() to use that attribute, and everything else uses _data().
Violate encapsulation and access the underlying $self->{_data} directly.
I tried an after '_load' => \&refresh;, but that just created an endless loop.
This would be a nice use of triggers:
has _data => (
is => 'ro',
lazy => 1,
builder => '_load',
trigger => sub { shift->refresh },
);
Except that triggers don't work on default/built values - only on values passed to the constructor explicitly, or passed to a writer/accessor method. Sad face. :-(
One solution would be to rewrite your refresh method so that instead of operating on $self->_data, it can accept a parameter (perhaps falling back to operating on $self->_data if no parameter is given.
sub _load {
my $self = shift;
my $tmp = retrieve $self->_file;
$self->refresh($tmp);
return $tmp;
}
sub refresh {
my $self = shift;
my $data = scalar(#_) ? $_[0] : $self->_data;
# do something with $data
}

Perl difficulty passing values to function

Cannot understand why the returned values from the function login bellow do not correspond to what is passed to it.
The following is a snippet of my code
package This_package;
.......
# returned from function that parses post data ($reqparam)
my $thisuser = $$reqparam{"username"};
# escape '#', username is an email
$thisuser =~ s/#/\#/;
my $thisuser_pass = $$reqparam{'password'};
print $thisuser; # ok
print $thisuser_pass; # ok
my $obj = new users;
my $valid_user = $obj->login($thisuser, $thisuser_pass);
.......
package Another_package;
sub new {
my ($class) = #_;
my $self = {
_login => undef,
_create_user => undef,
....
};
bless $self, $class;
return $self;
}
sub login ($$){
my ($user, $pass) = #_;
# some processing
.....
return $user; # prints users=HASH(...)
# return $pass; # prints the value of $user (the actual value)
# instead of the value of $pass
}
While trying to learn perl by converting some code from php into perl.
I have run into this problem, I have tried a few alternatives but obviously there is something I am not getting!
When you call a function like
my $valid_user = $obj->login($thisuser, $thisuser_pass);
The first parameter is this usually done as
sub login
{
my ( $self , $user , $password ) = #_;
}
You are missing $self
Because you are missing $self you user is actually the object and your password is actually the user.
If you are coming from another objected oriented language like C++ , Java or C#, this is a perl gotcha (no pun intended :)) . Another one is that even from an object method if you want to invoke another member method you have to use self like
$self->callAnotherObject( $user );
Simply calling wont do
callAnotherObject( $user );
Also I see that you are using function prototypes, It may not work as you intend it to be.
When you use object-oriented syntax ($obj->login($thisuser, $thisuser_pass)) to call a subroutine, the first argument will be the object itself. You should say, and you will typically see object-oriented modules use syntax like:
sub login {
my ($self, $user, $pass) = #_;
...
}
Incidentally, you shouldn't use prototypes ( ($$) ) without a good reason. Prototypes in Perl are not used in the same way they are in other languages, and in any case the prototype is ignored when you call a subroutine with indirect syntax (luckily, in your case, since you are actually calling it with 3 arguments).
You even watch Mythbusters?
Although you see Adam and Jamie do really, really dangerous stuff, they warn you at the beginning of every program, "Don't do this at home." Think of Perl prototypes in the same way. If you use them, there's a good likelihood you'll get badly burned.
Okay, now who is calling your login function? Or, maybe better, how is it called?
If I use your Perl module, do I call your login subroutine from my main program like this?
my $package_obj = Another_package->new;
$package_obj->login($user, $password);
Or, is this some subroutine that you use in your package for your convenience and you use it as a simple subroutine, and not a private method like this:
package Another_package;
sub new {
...
}
sub foo {
...
my $user = login ($user, $password);
}
If you're calling your login subroutine as a simple subroutine inside your package as in the second example, everything should be fine.
However, if you're treating your login subroutine like a full fledge method (like I do in the first example), you must remember that methods pass their class object as the first parameter of the subroutine.
Thus, you'll need to do something like this:
sub login {
my $self = shift; #Pointer to the Another_package object I'm using
my $user = shift;
my $password = shift; #I just love lining things up!
$self->{USER} = $user; #Bad way of doing it.
$self->{PASSWD} = $password;
... #Some processing.
return $user;
}
Why the #Bad way of doing it comment? Because you really want to keep your internals as separate as possible. That way, if you make a change to the structure of the Another_package class, your changes are isolated in a very specific part of your code. It makes debugging much easier.
A better way of writing the login subroutine would be:
sub Login { #In standard Perl, methods are capitalized.
my $self = shift; #Pointer to Another_package object
my $user = shift; #Allow user to pass user and password in constructor
my $password = shift; #I just love lining things up!
$self->User($user); #Way better: This is a setter/getter method
$self->Password($password);
... #Some processing.
return $user;
}
In this example, I'm using setter/getter methods for setting my user name and password. This way, I don't have to worry how they're actually stored in my object.
Here's your Another_Package module using setter/getter methods. I now allow the user to pass in the user and password when they call the new constructor if they'd like.
package Another_package;
sub new {
my $class = shift;
my $user = shift;
my $password = shift;
my $self = {};
bless $self, $class;
$self->User($user);
$self->Password($password);
...
return $self;
}
sub Login {
my $self = shift;
my $user = shift;
my $pass = shift;
$self->Password($pass);
if (not defined $self->User($user)) {
croak qq(Cannot log in without a user ID);
}
...
if ($login_successful) {
return $self->User; #Or maybe a session instant
else {
return;
}
}
Notice in my new constructor subroutine I create a $self anonymous hash (my $self = {}) and I immediately bless it. Now, $self is already a package object, and I can call a bunch of setter/getter methods to set the various fields in my object. My new constructor has no idea what my actual Another_module object looks like.
In my Login method subroutine, I also use the same setter/getter methods to set user and password. Again, my Login method knows nothing on how these fields are stored in the object.
One more thing you might notice is that I'm setting a scalar called $login_successful in my Login module to see whether or not my login was successful. In Perl, it is common to return nothing if the method fails, or return something on success. This way, the user's program can test to see if the call succeeded or failed. For example, maybe if the login fails, the user might want to try some default passwords before giving up:
my $package_obj = Another_package->new($user, $password);
my $foo = $package_obj->Login;
if (not defined $foo) {
foreach my $password qw(swordfish s3x mon3y 7ucky) {
$package_obj->Password($password);
last if $foo = $package_obj->Login;
}
if (not defined $foo) {
die "I don't know the password :-(";
}
}
So, what do my setter/getter methods look like? They're actually pretty simple:
sub User {
my $self = shift;
my $user = shift;
if(defined $user) {
$self->{USER_INFO}->{USER} = $user;
}
return $self->{USER_INFO}->{USER};
}
sub Password {
my $self = shift;
my $pass = shift;
if (defined $password) {
$self->{USER_INFO}->{PASSWORD} = $pass;
}
return $self->{USER_INFO}->{PASSWORD};
}
Why do I store $user in $self->{USER_INFO}->{USER} and not $self->{USER}? No reason a at all. However, it does show that the rest of the Another_package module doesn't care where or how I store the user and password.

Pass variables around the around method modifier

Is it possible to pass variables between multiple calls to the around MethodModier? example (that doesn't work but hopefully conveys what I want to do)
sub mysub { ... };
around 'mysub' => sub {
my $orig = shift;
my $self = shift;
my $value = get_value;
$self->orig(#_);
};
around 'mysub' => sub {
my $orig = shift;
my $self = shift;
my $value = shift;
my $output
= "sometext $value"
. $self->orig(#_);
. 'someothertext $value'
;
};
I'd eventually like to have these 'arounds' placed in pluggable traits, where I won't really know which ones are loaded beforehand but the final output will be neatly formatted.
It's possible that I'm thinking about this completely wrong, so other suggestions welcome.
What you are trying to do don't have logic.
"An around modifier receives the
original method as its first argument,
then the object, and finally any
arguments passed to the method."
https://metacpan.org/pod/Moose::Manual::MethodModifiers#BEFORE-AFTER-AND-AROUND
Use an instance variable:
$self->{value} = get_value;
...
my $value = $self->{value};
(See question commments for an actual answer. I'm just reiterating it here, so I can accept an answer, thanks to:
)

How can I call methods on a tied variable?

I've just started to learn about tie. I have a class named Link which I would like to do the following thing:
if fetched, return the link's address
if stored, store the new address
be able to call methods on it
So far, my code is :
package Link;
sub FETCH {
my $this = shift;
return $this->{"site"};
}
sub STORE {
my ($self,$site) = #_;
$self->{"site"} = $site;
}
sub print_method {
my $self = shift;
print $self->{"site"};
}
sub TIESCALAR {
my $class = shift;
my $link = shift;
my $this = {};
bless($this,$class);
$this->{"site"} = $link;
return $this;
}
1;
And the code I'm using to check the functionality is:
use Link;
tie my $var,"Link","http://somesite.com";
$var->print_method;
When ran, the script will terminate with the following error:
Can't call method "print_method" without a package or object reference at tietest.pl line 4..
If I understand its message correctly, $var->print_method resolves to some string upon which the method print_method is called. How could I benefit from tie, but also use the variable as an object?
EDIT: after experimenting a bit,I found out that if I return $self on fetch , I can call the methods , however , fetch won't return the address .
EDIT 2:the perl monks supplied me the solution : tied . tied will return a reference to the object VARIABLE .
By combining tied with my methods , I can accomplish everything I wanted .
Tie is the wrong tool for this job. You use ties when you want the same interface as normal data types but want to customize how the operations do their work. Since you want to access and store a string just like a scalar already does, tie doesn't do anything for you.
It looks like you want the URI module, or a subclass of it, and perhaps some overloading.
If you really need to do this, you need to use the right variable. The tie hooks up the variable you specify to the class you specify, but it's still a normal scalar (and not a reference). You have to use the object it returns if you want to call methods:
my $secret_object = tie my($normal_scalar), 'Tie::Class', #args;
$secret_object->print_method;
You can also get the secret object if you only have the tied scalar:
my $secret_object = tied $normal_scalar;
I have an entire chapter on tie in Mastering Perl.
I suggest making a normal Perl object and then overloading stringification. You lose the ability to store a value through assignment, but retain the ability to get the value out by printing the object. Once you start wanting to call methods directly, an object is probably what you want.
package Link;
use strict;
use Carp;
use overload
(
'""' => sub { shift->site },
fallback => 1,
);
sub new
{
my $class = shift;
my $self = bless {}, $class;
if(#_)
{
if(#_ == 1)
{
$self->{'site'} = shift;
}
else { croak "$class->new() expects a single URL argument" }
}
return $self;
}
sub site
{
my $self = shift;
$self->{'site'} = shift if(#_);
return $self->{'site'};
}
sub print_method
{
my $self = shift;
print $self->site, "\n";
}
1;
Example usage:
use Link;
my $link = Link->new('http://somesite.com');
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
If you really, really want assignment to work too, you can combine a normal object with overloaded stringification (Link, above) with tie:
package LinkTie;
use strict;
use Link;
sub FETCH
{
my $this = shift;
return $this->{'link'};
}
sub STORE
{
my($self, $site) = #_;
$self->{'link'}->site($site);
return $site;
}
# XXX: You could generalize this delegation with Class::Delegation or similar
sub print_method
{
my $self = shift;
print $self->{'link'}->print_method;
}
sub TIESCALAR
{
my $class = shift;
my $self = bless {}, $class;
$self->{'link'} = Link->new(#_);
return $self;
}
1;
Example usage:
tie my $link,'LinkTie','http://somesite.com';
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
$link = 'http://othersite.com';
print $link, "\n"; # http://othersite.com
$link->print_method; # http://othersite.com
This is all quite hideous and a long way to go just to get the dubious ability to assign to something that you can also call methods on and also print as-is. A standard URI object with stringification is probably a better bet.