Skip to content

Day 6 & 7 – Moose

Packages
|—Utils
|        |—Counter.pm

package Utils::Counter;
# FOR PERFORMANCE, DO NOT USE MOOSE ON THIS CLASS
# TESTS WITH MOOSE
#Files=4, Tests=18, 20 wallclock secs ( 0.07 usr  0.02 sys + 16.08 cusr  0.30 csys = 16.47 CPU)
# TESTS WITHOUT MOOSE
#Files=4, Tests=18,  6 wallclock secs ( 0.06 usr  0.03 sys +  5.20 cusr  0.28 csys =  5.57 CPU)

use strict;
use warnings;

sub new {
    my ( $class, $param1 ) = @_;
    
    my $self = {
        _startWith      => undef,
        _current        => undef,
        _increment      => 1,
        _maxIterations  => 100,
        _currIteration  => 0,
    };
        
    if(defined($param1)) {
        $self->{_startWith} = $param1->{start} if exists($param1->{startWith});
        
        $self->{_increment} = $param1->{increment} if exists($param1->{increment});
        
        $self->{_maxIterations} = $param1->{maxIterations} if exists($param1->{maxIterations});
    }
    
    bless $self, $class;
    return $self;
}


sub increment {
    my ( $self, $param1 ) = @_;
    $self->{_increment} = $param1 if defined($param1);
    return $self->{_increment};    
}

sub startWith {
    my ( $self, $param1 ) = @_;
    $self->{_startWith} = $param1 if defined($param1);
    return $self->{_startWith};    
}

sub maxIterations {
    my ( $self, $param1 ) = @_;
    $self->{_maxIterations} = $param1 if defined($param1);
    return $self->{_maxIterations};    
}

sub currIteration {
    my ( $self, $param1 ) = @_;
    $self->{_currIteration} = $param1 if defined($param1);
    return $self->{_currIteration};    
}

sub _init {
    my ( $self ) = @_;
    
    if(!defined($self->{_startWith})) {
        $self->{_startWith} = 1;
    }
 
    $self->{_currIteration} = 0;
    $self->{_current} = $self->{_startWith};
}

sub reset {
    my ( $self ) = @_;
    
    $self->{_currIteration} = 0;
    $self->{_current} = undef;
}

sub hasNext {
    my ( $self ) = @_;
    
    my $hasNext = 0;

    if($self->{_currIteration} < $self->{_maxIterations}) {
        $hasNext = 1;
    }
    
    return $hasNext;
}

sub getNext {
    my ( $self ) = @_;
       
    if(!defined($self->{_current})) {
        $self->_init();
    }
    else {
        $self->{_current} += $self->{_increment};
    }
    
    $self->{_currIteration}++;
        
    return $self->{_current};
}

1;

|        |—Web.pm

package Utils::Web;
use base 'Class::Singleton';

use HTTP::Request;
use HTTP::Response;
use LWP::UserAgent;

# this only gets called the first time instance() is called
sub new_instance {
    my $class = shift;
    my $self  = bless { }, $class;
    
    $self->{_userAgent} = LWP::UserAgent->new;
    $self->{_userAgent}->agent("Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)");

    return $self;
}


sub setProxy {
    my ( $self, $url, $user, $password ) = @_;
    
    $self->{_userAgent}->credentials( $url, '', $user, $password );
    $self->{_userAgent}->proxy(['http', 'ftp'], $url);
}


sub getPage{
    my ( $self, $url ) = @_;
    
    my $response = $self->{_userAgent}->request(HTTP::Request->new('GET', $url, [ 'Content-length' => 0]));
    
    return $response->content;
}

1;

|—Job
|    |—Site
|            |—Seek.pm

package Job::Site::Seek;
use Moose;

extends 'Job::Site';

use Utils::Web;

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

    $self->displayName("Seek - Melbourne");
    $self->url("http://www.seek.com.au");
    $self->searchString("/JobSearch?DateRange=31&location=1002&Keywords=<<KEYWORDS>>&page=<<COUNTER>>");
    $self->adString("/job/<<JOB_ID>>");
}

# Download and process a website
sub processSite {
    my ( $self ) = @_;
    
    my $content = undef;
    my $web = Utils::Web->new_instance();
    
    while($self->counter->hasNext()) {
        $content .= $web->getPage($self->_getNextPageUrl());
    }
    
    return $content;
}

no Moose;
__PACKAGE__->meta->make_immutable;

|        |—JobAd.pm

package Job::JobAd;
use Moose;
use Moose::Helper;

my $attrHash = {
    site   => { isa => 'Job::Site' },
    id   => {},
    url  => {},
    shortDesc => {},
    description => {},
};

createAccessors($attrHash, &has);

no Moose;
__PACKAGE__->meta->make_immutable;

|        |—Site.pm

package Job::Site;
use Moose;
use Moose::Helper;

use Utils::Web;
use Utils::Counter;

my $attrHash = {
    displayName   => {},
    url   => {},
    searchString  => {},
    adString => {},
    keywords => {},
    counter => {
        isa => 'Utils::Counter',
        lazy => 1,
        builder => 1,
    },
    pagesToDownload => {
        isa     => 'Num',
        trigger     => &_pagesToDownload_set,
    },
};

createAccessors($attrHash, &has);


#builder
sub _build_counter {
    my $self = shift;
     
    return Utils::Counter->new({startWith => 1, maxIterations => 10});
}

#trigger
sub _pagesToDownload_set {
    my ( $self, $new_value, $old_value ) = @_;
    
    # Update counter with new value
    $self->counter->maxIterations($new_value);
}


# Builds the url
sub _getNextPageUrl {
    my ( $self ) = @_;
    
    my $keywords = $self->keywords;
    my $counter = $self->counter->getNext();
    my $searchString = $self->searchString;
    $searchString =~ s/<<KEYWORDS>>/$keywords/;
    $searchString =~ s/<<COUNTER>>/$counter/;
    
    return $self->url . $searchString;
}


# Download and process a website
# Override this method on each subclass
sub processSite {
    my ( $self ) = @_;
    
    my $content = undef;
    my $web = Utils::Web->new_instance();
    
    while($self->counter->hasNext()) {
        my $nextp = $self->_getNextPageUrl();
        my $result = $web->getPage($nextp);
        $result = "ERROR - Could not get the page [$nextp]n" if !defined($result);
        
        $content .= $result;
    }
    
    return $content;
}

no Moose;
__PACKAGE__->meta->make_immutable;

|—Moose
|        |—Helper.pm

package Moose::Helper;
use base "Exporter";
@EXPORT = qw(createAccessors);

sub createAccessors {
    my ($attrHash, $has, $default) = @_;
    
    $default = {} if(!defined($default));
    $default->{is} = 'rw' if(!defined($default->{is}));
    $default->{isa} = 'Str' if(!defined($default->{isa}));

    foreach my $attr (keys %$attrHash) {
        my $values = $attrHash->{$attr};

        $values->{is} = $default->{is} if(!defined($values->{is}));
        $values->{isa} = $default->{isa} if(!defined($values->{isa}));
        $values->{builder} = '_build_' . $attr if(defined($values->{builder}) && $values->{builder} == 1);

       $has->($attr => %$values );
    }
}

1;

||—Makefile.PL

use ExtUtils::MakeMaker;
WriteMakefile(
    test        => {TESTS => 't/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t',}
);

||—run_tests.sh

perl Makefile.PL
make test

# Clean
make clean >>/dev/null

t
|—Test
|    |—Utils
|            |—Counter.t

#!/usr/bin/perl

use lib 'Packages';

use strict;
use Test::More 0.88;

require Utils::Counter;

my $counter = new_ok('Utils::Counter');

$counter->increment(0.3567);
$counter->startWith(.325);
$counter->maxIterations(11);

my $c = $counter->startWith();

while($counter->hasNext()) {
    is($counter->getNext(), $c, "Counter $c OK");
    $c += $counter->increment();
}

# Test a big limit
my $i = 0;
my $infinit = 1000000;
$counter->reset();
$counter->maxIterations($infinit);

while($counter->hasNext()) {
    $i++;
    $counter->getNext();
}

is($i, $infinit, "Iterator limiter works OK");

my $expected = $counter->startWith() + ($counter->currIteration() * $counter->increment() );
is(sprintf("%.4f", $counter->getNext()), sprintf("%.4f", $expected), "Counter counted until $expected OK");

done_testing;

|            |—MockWeb.pm

package Test::Utils::MockWeb;

use strict;
use warnings;

sub new {
    my ($class) = @_;

    my $self = {};

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

sub setPageResponse {
    my ( $self, $url, $response ) = @_;

    $self->{$url} = $response;

}

sub getPage{
    my ( $self, $url ) = @_;

    return $self->{$url};
}

1;

|    |—Builders
|            |—Counter.pm

Package Builder::Counter;

use lib "../../../Packages";

use strict;
use base 'Utils::Counter';

sub new {
    my ( $class ) = @_;
    
    my $self = {
        _counter => Utils::Counter->new(),
    };
    
    bless $self, $class;
    return $self;
}

sub build {
    my $self = shift; 
    return $self->{_counter};
}

sub withIncrement {
    my $self = shift; 
    my $params = @_;
    $self->{_counter}->increment($params);
    return $self;    
}

sub withStart {
    my $self = shift; 
    my $params = @_;
    $self->{_counter}-startWith($params);
    return $self;    
}

sub withMaxIterations {
    my $self = shift; 
    my $params = @_;
    $self->{_counter}->maxIterations($params);
    return $self;    
}

1;

|    |—Job
|        |—Site
|                |—Seek.t

#!/usr/bin/perl

use lib 'Packages';
use lib 't';

use strict;
use Test::More 0.88;
use Test::MockModule;
use Test::Utils::MockWeb;
require Job::Site::Seek;

######################################################################
# BEGIN MOCK OBJECTS DEFINITION
my $customWeb = Test::Utils::MockWeb->new();

my $module = new Test::MockModule('Utils::Web');
$module->mock(getPage => sub { 
        my ($self, $url) = @_ ;
        return $customWeb->getPage($url)
    });

# END MOCK OBJECTS DEFINITION
######################################################################


######################################################################
# BEGIN TESTS
# Test
subtest 'Test Processing Pages' => sub {
    my $seek = new_ok( 'Job::Site::Seek' );

    # Get getNextPageUrl
    my $keywords = "TEST THIS";
    my $url = $seek->url();
    my $searchString = $seek->searchString();
    
    $searchString =~ s/<<KEYWORDS>>/$keywords/;
    
    
    my $expected_url = [
        $url . $searchString =~ s/<<COUNTER>>/1/r,
        $url . $searchString =~ s/<<COUNTER>>/2/r,
    ];

    my $expected_content = [
        "PAGE1",
        "PAGE2",
    ];

    # Setup Site
    $seek->pagesToDownload(2);
    $seek->keywords($keywords);
   
    $customWeb->setPageResponse($expected_url->[0], $expected_content->[0]);
    $customWeb->setPageResponse($expected_url->[1], $expected_content->[1]);

    my $expected_result = $expected_content->[0] . $expected_content->[1];

    is($seek->processSite(), $expected_result, "processSite ok");
};

# END TESTS
######################################################################
done_testing;

|            |—JobAd.t

#!/usr/bin/perl

use lib "Packages";

use strict;
use Test::More 0.88;
use Job::Site;

require Job::JobAd;

######################################################################
# BEGIN TESTS
subtest 'Testing accessors' => sub {
    my $jobAd = new_ok( 'Job::JobAd' );
    my $test_value = "test_value";

    # Test accessors
    my $test_site = Job::Site->new();
    $jobAd->site($test_site);
    is($jobAd->site(), $test_site, "Site accessor ok");

    $jobAd->id($test_value);
    is($jobAd->id(), $test_value, "id accessor ok");

    $jobAd->url($test_value);
    is($jobAd->url(), $test_value, "url accessor ok");

    $jobAd->shortDesc($test_value);
    is($jobAd->shortDesc(), $test_value, "shortDesc accessor ok");

    $jobAd->description($test_value);
    is($jobAd->description(), $test_value, "description accessor ok");
};

# END TESTS
######################################################################
done_testing;

|            |—Site.t

#!/usr/bin/perl

use lib 'Packages';
use lib 't';

use strict;
use Test::More 0.88;
use Test::MockModule;
use Test::Utils::MockWeb;
require Job::Site;

######################################################################
# BEGIN MOCK OBJECTS DEFINITION
my $customWeb = Test::Utils::MockWeb->new();

my $module = new Test::MockModule('Utils::Web');
$module->mock(getPage => sub { 
        my ($self, $url) = @_ ;
        return $customWeb->getPage($url)
    });

# END MOCK OBJECTS DEFINITION
######################################################################


######################################################################
# BEGIN TESTS
# Test
subtest 'Testing accessors' => sub {
    my $site = new_ok( 'Job::Site' );
    my $test_value = "test_value";

    # Test accessors
    $site->displayName($test_value);
    is($site->displayName(), $test_value, "displayName accessor ok");

    $site->url($test_value);
    is($site->url(), $test_value, "url accessor ok");
        
    $site->searchString($test_value);
    is($site->searchString(), $test_value, "searchString accessor ok");

    $site->adString($test_value);
    is($site->adString(), $test_value, "adString accessor ok");

    $site->keywords($test_value);
    is($site->keywords(), $test_value, "keywords accessor ok");

    $test_value = 100;
    $site->pagesToDownload($test_value);
    is($site->pagesToDownload(), $test_value, "pagesToDownload accessor ok");

    my $test_counter = Utils::Counter->new();
    $site->counter($test_counter);
    is($site->counter(), $test_counter, "counter accessor ok");
};

subtest 'Test Processing Pages' => sub {
    my $site = new_ok( 'Job::Site' );
    
    my $keywords = "TEST THIS";
    my $url = "my.com";
    my $searchString = "/search=<<KEYWORDS>>&page=<<COUNTER>>";
    
    my $expected_url = [
        $url . "/search=" . $keywords . "&page=1",
        $url . "/search=" . $keywords . "&page=2",
    ];
    
    my $expected_content = [
        "PAGE1",
        "PAGE2",
    ];

    # Setup Site
    $site->url($url);
    $site->searchString($searchString);
    $site->pagesToDownload(2);
    $site->keywords($keywords);
   
    $customWeb->setPageResponse($expected_url->[0], $expected_content->[0]);
    $customWeb->setPageResponse($expected_url->[1], $expected_content->[1]);

    my $expected_result = $expected_content->[0] . $expected_content->[1];

    is($site->processSite(), $expected_result, "processSite ok");
};

# END TESTS
######################################################################
done_testing;

.

Leave a Comment

Leave a comment