Question

I'm working with WWW::Mechanize to automate web-based back office clicking I need to do to get my test e-commerce orders into the state I need them to be to test changes I have made to a particular part of a long, multi-part workflow. To process a lot of orders in a batch, I need to click the Home link often. To make that shorter, I hacked a method into WWW::Mechanize at run time like this (based on an example in Mastering Perl by brian d foy):

{ # Shortcut to go back to the home page by calling $mech->go_home
  # I know I'll get a warning and do not want it!
  no warnings 'once';
  my $homeLink = $mech->find_link( text => 'Home' )->url_abs();
  $homeLink =~ s/system=0/system=1/;
  *WWW::Mechanize::go_home = sub {
    my ($self) = @_;
    return $self->get($homeLink);
  };
}

This works great, and does not hurt anyone because the script I'm using it in is only used by me and is not part of the larger system.

But now I wonder if it is possible to actually only tell one $mech object that is has this method, while another WWW::Mechanize object that might be created later (to, say, do some cross-referencing without mixing up the other one that has an active session to my back office) cannot use that method.

I'm not sure if that is possible at all, since, if I understand the way objects work in Perl, the -> operator tells it to look for the subroutine go_home inside the package WWW::Mechanize and pass the $mech as the first argument to it. Please correct me if this understanding is wrong.

I've experimented by adding a sort of hard-coded check that only lets the original $mech object use the function.

my $onlyThisMechMayAccessThisMethod = "$mech";
my $homeLink = $mech->find_link( text => 'Home' )->url_abs();
$homeLink =~ s/system=0/system=1/;
*WWW::Mechanize::go_home = sub {
  my ($self) = @_;
  return undef unless $self eq $onlyThisMechMayAccessThisMethod;
  return $self->get($homeLink);
};

Since "$mech" contains the address of where the data is stored (e.g. WWW::Mechanize=HASH(0x2fa25e8)), another object will look differently when stringified this way.

I am not convinced however that this is the way to go. So my question is: Is there a better way to only let one object of the WWW::Mechanize class have this method? I'm also glad about other suggestions regarding this code.

Was it helpful?

Solution

This is just

$mech->follow_link(text => 'Home')

and I don't think it's special enough to warrant a method of its own, or to need restricting to an exclusive club of objects.

It's also worth noting that there is no need to mess with typeglobs to declare a subroutine in a different package. You just have to write, for example

sub WWW::Mechanize::go_home {
    my ($self) = @_;
    return $self->get($homeLink);
};

But the general solution is to subclass WWW::Mechanize and declare as members only those objects you want to have the new method.


File MyMechanize.pm

package MyMechanize;

use strict;
use warnings;

use parent 'WWW::Mechanize';

sub go_home {
  my $self = shift;
  my $homeLink = $self->find_link(text => 'Home')->url_abs;
  $homeLink =~ s/system=0/system=1/;
  return $self->get($homeLink);
}

1;

File test.pl

use strict;
use warnings;

use MyMechanize;

my $mech = MyMechanize->new;
$mech->get('http://mydomain.com/path/to/site/page.html')
$mech->go_home;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top