Question

I simply hate how CGI::Application's accessor for the CGI object is called query.

I would like my instance classes to be able to use an accessor named cgi to get the CGI object associated with the current instance of my CGI::Application subclass.

Here is a self-contained example of what I am doing:

package My::Hello;

sub hello {
    my $self =shift;
    print "Hello @_\n";
}

package My::Merhaba;

use base 'My::Hello';

sub merhaba {
    goto sub { shift->hello(@_) };
}

package main;

My::Merhaba->merhaba('StackOverflow');

This is working as I think it should and I cannot see any problems (say, if I wanted to inherit from My::Merhaba: Subclasses need not know anything about merhaba).

Would it have been better/more correct to write

sub merhaba {
    my $self = shift;
    return $self->hello(@_);
}

What are the advantages/disadvantages of using goto &NAME for the purpose of aliasing a method name? Is there a better way?

Note: If you have an urge to respond with goto is evil don't do it because this use of Perl's goto is different than what you have in mind.

Was it helpful?

Solution

Your approach with goto is the right one, because it will ensure that caller / wantarray and the like keep working properly.

I would setup the new method like this:

sub merhaba {
    if (my $method = eval {$_[0]->can('hello')}) {
        goto &$method
    } else { 
        # error code here
    }
}

Or if you don't want to use inheritance, you can add the new method to the existing package from your calling code:

*My::Hello::merhaba = \&My::Hello::hello;  
   # or you can use = My::Hello->can('hello');

then you can call:

My::Hello->merhaba('StackOverflow');

and get the desired result.

Either way would work, the inheritance route is more maintainable, but adding the method to the existing package would result in faster method calls.

Edit:

As pointed out in the comments, there are a few cases were the glob assignment will run afoul with inheritance, so if in doubt, use the first method (creating a new method in a sub package).

Michael Carman suggested combining both techniques into a self redefining function:

sub merhaba {
    if (my $method = eval { $_[0]->can('hello') }) {
        no warnings 'redefine';
        *merhaba = $method;
        goto &merhaba;
    }
    die "Can't make 'merhaba' an alias for 'hello'";
}

OTHER TIPS

You can alias the subroutines by manipulating the symbol table:

*My::Merhaba::merhaba = \&My::Hello::hello;

Some examples can be found here.

I'm not sure what the right way is, but Adam Kennedy uses your second method (i.e. without goto) in Method::Alias (click here to go directly to the source code).

This is sort of a combination of Quick-n-Dirty with a modicum of indirection using UNIVERSAL::can.

package My::Merhaba;
use base 'My::Hello';
# ...
*merhaba = __PACKAGE__->can( 'hello' );

And you'll have a sub called "merhaba" in this package that aliases My::Hello::hello. You are simply saying that whatever this package would otherwise do under the name hello it can do under the name merhaba.

However, this is insufficient in the possibility that some code decorator might change the sub that *My::Hello::hello{CODE} points to. In that case, Method::Alias might be the appropriate way to specify a method, as molecules suggests.

However, if it is a rather well-controlled library where you control both the parent and child categories, then the method above is slimmmer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top