Monday, November 30, 2009

The Lazy Moose - Finale!


When Laziness is a Virtue - Part 03 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl.

My implementation consists of three class. Four, if you consider inheritance. The main class is EarnedValue - I call it main as this class exposes the application API. That's the main point.

The two other classes manage the work, divided along the lines of schedule and cost - handling and processing. The class for schedule is Schedule and cost is Cost. Schedule works off the Gantt chart whereas Cost works off the time-log. (Both of these are described in Part 02.)

So where would you expect to find the basic functionality for pulling data out of a spreadsheet? In Cost and Schedule, right?

My implementation actually deposits this functionality in EarnedValue - the main class. Why do I do that? Partly, to avoid duplication of code. I know that both Cost and Schedule require this functionality - so I put it in a third place, and give both these clients hooks into that functionality by passing a reference. So whenever data is to be pulled off a spreadsheet, the client object delegates the fetching operation to the main class.

If you've been following my other presentations in this series, you would think that I need to use Roles, no? Well, that's exactly what I have done here. I haven't used Roles in the purist, didactic sense, of course - which is why you don't see 'Moose::Role' anywhere. Remember, like much of everything else in the Object-oriented way of going about life, Roles are about the concept rather than a specific, narrowly-defined implementation.

The Japanese have a saying "Bolt what you can weld, tape what you can bolt, hold what you can tape." When you see a concept, free yourself from any particular implementation of it. That way, you can do much more. Even though it may not look like you are doing much at all.

So here's the output:

And the code the EarnedValue class that generates it - it's a bunch of code, an eyeful, and please follow it with the handy 10-step guide below :o)

[1.]
First the business, as usual. 'use Moose' and all that. Par for the course. The exception handling is based on object-oriented Exception::Class. Following best-practices (documented in this blog), the $SIG{__DIE__} event-handler is overloaded.

package EarnedValue;
use Moose;
use OLE;
use Win32::OLE::Const "Microsoft Excel";
use File::Spec;
use Moose::Util::TypeConstraints;
use Params::Coerce ();
use Data::Dumper;

use Exception::Class (
'EV::Exception::Base' => {
description => 'Base Exception Class',
},
'EV::Exception::Base::Logger' => {
isa => 'EV::Exception::Base',
description => 'Simply logging!',
},
'EV::Exception::Base::InvalidState' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Invalid state',
},
'EV::Exception::Base::MissingData' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Incomplete information',
},
);

local $SIG{__DIE__} = sub {
my $err = shift;
if ($err->isa('EV::Exception::Base')) {
die $err;
} elsif ($err->isa('Cost::Exception::Base')) {
die $err;
} elsif ($err->isa('Schedule::Exception::Base')) {
die $err;
} else {
EV::Exception::Base->throw($@);
}
print $@;
};

[2.]
Data - Sources of information: (a.) Spreadsheet_Schedule, (b.) Spreadsheet_Cost, and (c.) activeSpreadsheet.
The results are generated by scraping these two spreadsheets. Naturally, these need to be supplied by the user. Note that a constraint is set on the slot via subtype, to enforce that the user-supplied document exists on disk. If not, exception is thrown.

subtype 'extantFile'
=> as Str
=> where {
( -e $_ );
};

has 'activeSpreadsheet' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_activeSpreadsheet',
# trigger => \&timeStamped_copy
);

has 'Spreadsheet_Schedule' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetSchedule',
default => \&evGantt,
);

has 'Spreadsheet_Cost' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetCost',
default => \&evBasecamp,
);

[3.]
Data - Results-on-demand: EV, PV, AC, SPI, CPI
The meat! These slots hold the results of scraping spreadsheets and other skulldruggery. The mother-lode, if you will. Note that each has attribute 'lazy' set to '1', implementing lazy-loading. The 'default' attribute of each variable is set to a method that - this is how the chain of events is triggered when a value is demanded.

has 'EV' => (
is => 'rw',
isa => 'Num',
default => \&calculateEV,
lazy => 1,
);

has 'PV' => (
is => 'rw',
isa => 'Num',
default => \&calculatePV,
lazy => 1,
);

has 'AC' => (
is => 'rw',
isa => 'Num',
default => \&calculateAC,
lazy => 1,
);

has 'SPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateSPI,
lazy => 1,
);

has 'CPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateCPI,
lazy => 1,
);

[4.]
Objects - MySchedule, MyCost
These are slots for objects encapsulating the requisite functionality for handling and processing time and cost information. Information is parceled into neat intermediate components here for assembly in the final step by main (EarnedValue) class.

has 'MySchedule' => (
is => 'rw',
isa => 'Schedule',
);

has 'MyCost' => (
is => 'rw',
isa => 'Cost',
);

[5.]
Data - Diary
This is an interesting one. It goes with an associated method 'log_Me', which is the exception handler for the application. Any planned exceptions are handled here and a log of key events with time-date stamp is maintained. The slot is for the log-file that 'log_Me' utilizes.

has 'Diary' => (
is => 'rw',
isa => 'Str',
default => \&evLog,
predicate => 'has_Diary',
clearer => 'clear_Diary',
);

[6.]
Data - Other
Note the use of subtype to enforce a constraint. The specification of cell range is constrained by subtype that checks by a regular expression. The range of cells associated with an Excel spreadsheet query is stashed away here for use by querying methods.

subtype 'CellRange'
=> as Str
=> where {
( /^[a-zA-Z]{1,2}\d+$/ || /^[a-zA-Z]{1,2}\d+?:[a-zA-Z]{1,2}\d+$/);
};

has 'Range' => (
is => 'rw',
# isa => 'CellRange',
isa => 'Str',
predicate => 'has_Range',
clearer => 'clear_Range',
);

has 'multiplying_factor' => (
is => 'rw',
isa => 'Num',
default => 1000,
);

[7.]
Methods - calculateEV, calculatePV, calculateAC, calculateSPI, calculateCPI
The high-level methods recruit these middle-level methods. The structure set-up by these methods is critical. As mention in [3.], each method implements a 'default' attribute set upon a lazy variable. What does that mumbo-jumbo mean? Only that the method is fired automatically when the slot is accessed the first time.
Note the exception handling mechanism. The 'log_Me' method is invoked both for logging and handling any exception originating from the invocation of a high-level method.

sub calculateEV {
my $self = shift;
my $EV;
eval {
$EV = $self->MySchedule->tally_EVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}

return $EV;
};

sub calculatePV {
my $self = shift;
my $PV;
eval {
$PV = $self->MySchedule->tally_PVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}

return $PV;
};

sub calculateAC {
my $self = shift;
my $AC;

eval {
$AC = $self->MyCost->tally_ACnumbers;
};
my $e;
if ($e = Cost::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
};

return $AC;
};

sub calculateSPI {
my $self = shift;
my ($netEV,$netPV);
my $SPI;
eval {
$netEV = $self->EV;
$netPV = $self->PV;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$SPI = $netEV/$netPV;
};

return $SPI;
};

sub calculateCPI {
my $self = shift;
my ($netEV,$netAC);
my $CPI;
eval {
$netEV = $self->EV;
$netAC = $self->AC;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$CPI = $netEV/$netAC;
};

return $CPI;
};

[8.]
Methods - printExecutiveSummary, printCostStructure
The high-level methods - for formatted reporting. The output generated is a report with a very simple structure. It is easy to see how this can be pumped up with all the bling from Google visualization API and other JavaScript/Ajay frameworks.
 
sub printExecutiveSummary {
my $self = shift;
printf("%10s %12s\n", 'ITEM', 'INR Value');
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'EV', $self->EV);
printf("%10s %12.2f\n", 'PV', $self->PV);
printf("%10s %12.2f\n", 'AC', $self->AC);
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'SPI', $self->SPI);
printf("%10s %12.2f\n", 'CPI', $self->CPI);
printf("%10s-%12s\n", '----------', '------------');
print "\n";
$self->log_Me if $self->has_Diary;
};

sub printCostStructure {
my $self = shift;
my $dashboard;
return unless $self->MyCost->can('ActivityCost');
$dashboard = $self->MyCost->ActivityCost;

printf(" %10s %12s\n", 'ACTIVITY', 'INR Value');
printf(" %10s-%12s\n", '----------', '------------');
my @theItems = sort(keys(%{$dashboard}));
foreach my $thisItem (@theItems) {
printf(" %-10s %12.2f\n", $thisItem, $dashboard->{$thisItem});
};
printf(" %10s-%12s\n", '----------', '------------');
return $dashboard;
}

[9.]
Methods - queryMe, _querySpreadsheet_singleCell, _querySpreadsheet_Range
The are querying methods that use Win32 automation (Win32::OLE) to work with Microsoft Excel data. The wrapper method 'queryMe' is invoked with the value stashed in 'Range' and returns the content as a data-structure (reference). It deploys the internal 'helper' functions (name beginning with an underscore) where the spreadsheet operations actually take place. The wrapper determines what is the appropriate functionality to use based on the arguments passed.
 
sub queryMe {
# method returns contents of a cell or the cells in a specified range as 2D array.
my $self = shift; my $sheet = shift;
return unless $self->has_Range;
$sheet = 1 unless $sheet;

my $myRange = $self->Range;
if ($myRange =~ /:/ ) {
$self->_querySpreadsheet_Range($myRange, $sheet);
} else {
$self->_querySpreadsheet_singleCell($myRange, $sheet);
};

};

sub _querySpreadsheet_singleCell {
# method returns contents of a single cell.
my $self = shift;
my $cell = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;

my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;

my $workbook = $excel->Workbooks->Open($source) || die "Unable to open!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);

my $data = $evSheet->Range($cell); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $cell . ' | ' . $dataValue . ' |';
$workbook->Close;

return $dataValue;
};

sub _querySpreadsheet_Range {
# method returns contents of a range of cells (2D array).
my $self = shift;
my $range = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;

my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;

my $workbook = $excel->Workbooks->Open($source) || die "Unable to open ($source)!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);

my $data = $evSheet->Range($range); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $range . ' |';
$workbook->Close;

return $dataValue;
};

[10.]
Methods - log_Me
This is an Exception Central - uses Exception::Class stack-trace functionality to record logs and (of course) to imprint the stack trace upon the logs when exceptions occur and the code breaks. Did you notice 'EV::Exception::Base::Logger' in Exception::Class? That's what this is about.
 
sub log_Me {
my $self = shift;
my $e = shift;
my $diary = $self->Diary;
my $eMsg = '';

open(INFO, ">>$diary");
unless ($e) {
print INFO &timeStamp, " OK ";
eval {
EV::Exception::Base::Logger->throw;
};
my $e;
if ($e = EV::Exception::Base::Logger->caught) {
my @lines = ();
foreach my $thisLine (split("\n", $e->trace)) {
push @lines, $thisLine if ($thisLine =~ 'EarnedValue::');
}
print INFO join(' | ', @lines);
} #fi
} elsif ($e->isa('Cost::Exception::Base')) {
$eMsg = Cost::Exception::Base->description . " -> ";
$eMsg .= Cost::Exception::Base::MissingData->description
if $e->isa('Cost::Exception::Base::MissingData');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
} elsif ($e->isa('Schedule::Exception::Base')) {
$eMsg = Schedule::Exception::Base->description . " ";
$eMsg .= Schedule::Exception::Base::InvalidState->description
if $e->isa('Schedule::Exception::Base::InvalidState');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
}
close INFO;
}


So how did laziness become a virtue in these 10 simple steps? The complete code is provided below, and may also be downloaded from the site. We have seen how all computation is triggered by access - Just In Time (JIT). No computational resources are wasted loading data that is not required. No inventory is maintained, unless required. Let's reinforce this with a scenario:

1. A request for SPI (EarnedValue::SPI)..
2. triggers a JIT request for EV, PV (EarnedValue::EV, EarnedValue::PV) ..
3. setting off Schedule accessors: Schedule::EVnumbers, Schedule::PVnumbers
4. that consume Roles defined in the main EarnedValue class: EarnedValue::queryMe
5. to pull data off spreadsheet into Object slots:
Schedule::EVnumbers, Schedule::PVnumbers
6. for processing into interim results via Schedule methods:
Schedule::tally_EVnumbers, Schedule::tally_PVnumbers
7. which are then stuffed into slots in the main EarnedValue class:
EarnedValue::EV, EarnedValue::PV
8. thus stuffing the slot EarnedValue::SPI
9. and producing the desired result.

The complete class-def for EarnedValue is reproduced below. For Schedule and Cost implementation, please refer the code posted on the Google site (link at the end of page).

Here is what the complete EarnedValue module looks like:

package EarnedValue;
use Moose;
use OLE;
use Win32::OLE::Const "Microsoft Excel";
use File::Spec;
use Moose::Util::TypeConstraints;
use Params::Coerce ();
use Data::Dumper;

use Exception::Class (
'EV::Exception::Base' => {
description => 'Base Exception Class',
},
'EV::Exception::Base::Logger' => {
isa => 'EV::Exception::Base',
description => 'Simply logging!',
},
'EV::Exception::Base::InvalidState' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Invalid state',
},
'EV::Exception::Base::MissingData' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Incomplete information',
},
);

local $SIG{__DIE__} = sub {
my $err = shift;
if ($err->isa('EV::Exception::Base')) {
die $err;
} elsif ($err->isa('Cost::Exception::Base')) {
die $err;
} elsif ($err->isa('Schedule::Exception::Base')) {
die $err;
} else {
EV::Exception::Base->throw($@);
}
print $@;
};

subtype 'extantFile'
=> as Str
=> where {
( -e $_ );
};

has 'activeSpreadsheet' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_activeSpreadsheet',
# trigger => \&timeStamped_copy
);

has 'Spreadsheet_Schedule' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetSchedule',
default => \&evGantt,
);

has 'Spreadsheet_Cost' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetCost',
default => \&evBasecamp,
);

has 'Diary' => (
is => 'rw',
isa => 'Str',
default => \&evLog,
predicate => 'has_Diary',
clearer => 'clear_Diary',
);

has 'EV' => (
is => 'rw',
isa => 'Num',
default => \&calculateEV,
lazy => 1,
);

has 'PV' => (
is => 'rw',
isa => 'Num',
default => \&calculatePV,
lazy => 1,
);

has 'AC' => (
is => 'rw',
isa => 'Num',
default => \&calculateAC,
lazy => 1,
);

has 'SPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateSPI,
lazy => 1,
);

has 'CPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateCPI,
lazy => 1,
);

has 'MySchedule' => (
is => 'rw',
isa => 'Schedule',
);

has 'MyCost' => (
is => 'rw',
isa => 'Cost',
);

has 'multiplying_factor' => (
is => 'rw',
isa => 'Num',
default => 1000,
);

subtype 'CellRange'
=> as Str
=> where {
( /^[a-zA-Z]{1,2}\d+$/ || /^[a-zA-Z]{1,2}\d+?:[a-zA-Z]{1,2}\d+$/);
};

has 'Range' => (
is => 'rw',
# isa => 'CellRange',
isa => 'Str',
predicate => 'has_Range',
clearer => 'clear_Range',
);

sub calculateEV {
my $self = shift;
my $EV;
eval {
$EV = $self->MySchedule->tally_EVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}

return $EV;
};

sub calculatePV {
my $self = shift;
my $PV;
eval {
$PV = $self->MySchedule->tally_PVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}

return $PV;
};

sub calculateAC {
my $self = shift;
my $AC;

eval {
$AC = $self->MyCost->tally_ACnumbers;
};
my $e;
if ($e = Cost::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
};

return $AC;
};

sub calculateSPI {
my $self = shift;
my ($netEV,$netPV);
my $SPI;
eval {
$netEV = $self->EV;
$netPV = $self->PV;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$SPI = $netEV/$netPV;
};

return $SPI;
};

sub calculateCPI {
my $self = shift;
my ($netEV,$netAC);
my $CPI;
eval {
$netEV = $self->EV;
$netAC = $self->AC;
};
my $e;
if (($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$CPI = $netEV/$netAC;
};

return $CPI;
};

sub printExecutiveSummary {
my $self = shift;
printf("%10s %12s\n", 'ITEM', 'INR Value');
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'EV', $self->EV);
printf("%10s %12.2f\n", 'PV', $self->PV);
printf("%10s %12.2f\n", 'AC', $self->AC);
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'SPI', $self->SPI);
printf("%10s %12.2f\n", 'CPI', $self->CPI);
printf("%10s-%12s\n", '----------', '------------');
print "\n";
$self->log_Me if $self->has_Diary;
};

sub printCostStructure {
my $self = shift;
my $dashboard;
return unless $self->MyCost->can('ActivityCost');
$dashboard = $self->MyCost->ActivityCost;

printf(" %10s %12s\n", 'ACTIVITY', 'INR Value');
printf(" %10s-%12s\n", '----------', '------------');
my @theItems = sort(keys(%{$dashboard}));
foreach my $thisItem (@theItems) {
printf(" %-10s %12.2f\n", $thisItem, $dashboard->{$thisItem});
};
printf(" %10s-%12s\n", '----------', '------------');
return $dashboard;
}

sub evGantt {
my $folder = File::Spec->curdir();
my $file = q{Dashboard.xls};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
};

sub evBasecamp {
my $folder = File::Spec->curdir();
my $file = q{time-export.csv};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
}

sub evLog {
my $folder = File::Spec->curdir();
my $file = q{main.log};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
};

sub timeStamped_copy {
# for safety, make a time-stamped copy before the spreadsheet is accessed.
my $self = shift;
};

sub queryMe {
# method returns contents of a cell or the cells in a specified range as 2D array.
my $self = shift; my $sheet = shift;
return unless $self->has_Range;
$sheet = 1 unless $sheet;


my $myRange = $self->Range;
if ($myRange =~ /:/ ) {
$self->_querySpreadsheet_Range($myRange, $sheet);
} else {
$self->_querySpreadsheet_singleCell($myRange, $sheet);
};

};

sub _querySpreadsheet_singleCell {
# method returns contents of a single cell.
my $self = shift;
my $cell = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;

my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;

my $workbook = $excel->Workbooks->Open($source) || die "Unable to open!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);

my $data = $evSheet->Range($cell); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $cell . ' | ' . $dataValue . ' |';
$workbook->Close;

return $dataValue;
};

sub _querySpreadsheet_Range {
# method returns contents of a range of cells (2D array).
my $self = shift;
my $range = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;

my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;

my $workbook = $excel->Workbooks->Open($source) || die "Unable to open ($source)!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);

my $data = $evSheet->Range($range); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $range . ' |';
$workbook->Close;

return $dataValue;
};

sub log_Me {
my $self = shift;
my $e = shift;
my $diary = $self->Diary;
my $eMsg = '';

open(INFO, ">>$diary");
unless ($e) {
print INFO &timeStamp, " OK ";
eval {
EV::Exception::Base::Logger->throw;
};
my $e;
if ($e = EV::Exception::Base::Logger->caught) {
my @lines = ();
foreach my $thisLine (split("\n", $e->trace)) {
push @lines, $thisLine if ($thisLine =~ 'EarnedValue::');
}
print INFO join(' | ', @lines);
} #fi
} elsif ($e->isa('Cost::Exception::Base')) {
$eMsg = Cost::Exception::Base->description . " -> ";
$eMsg .= Cost::Exception::Base::MissingData->description
if $e->isa('Cost::Exception::Base::MissingData');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
} elsif ($e->isa('Schedule::Exception::Base')) {
$eMsg = Schedule::Exception::Base->description . " ";
$eMsg .= Schedule::Exception::Base::InvalidState->description
if $e->isa('Schedule::Exception::Base::InvalidState');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
}
close INFO;
}

sub timeStamp {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
my $tstamp = sprintf
"\[%02d\-%02d\-%4d : %02d\-%02d\-%02d\]",
mon+1,$mday,$year+1900,$hour,$min,$sec;
return $tstamp;
}

All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1

The Lazy Moose - Part 02 of 03

When Laziness is a Virtue - Part 02 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl.

For Earned Value Analysis, my data resides in two spreadsheets. The first one is 'Dashboard.xls' - an important-looking spreadsheet with a bevy of beautiful numbers housed in multicolored cells and even the solid gray bars have a blue overlay .. in short, everything calculated to impress the bosses. It is variously known as Gantt Chart or Bar Chart and is basically a schedule with workdays along the X-axis and project activities organized into packages along the Y-axis. So the gray bars tell about the time-lines for each activity under each work package and the blue overlay tells how much (%-age wise) has been accomplished out of the total in the gray. That piece of information (%-age complete) goes in the red column (H) and the spreadsheet automatically updates itself, blue bars and all - savvy?

(Making such a spreadsheet is an interesting story in itself involving utilization of the full date-time capabilities of MS-Excel and a neat little trick called Conditional Formatting - which has been pushed to the envelope. But that's another story for another day.)


That's all very well for time, (she says, slipping into a diaphanous negligee,) but where is the information upon costs? Sharp! There's one more spreadsheet that has those numbers, and for faint of heart it ain't.



Actually, all it is is my operators and how they have charged time to the project, on the basis of which I estimate costs. Samuel Jackson, for example, has been working his butt off whereas Morgan Freeman has been idly sitting on his.

If you're keeping up with me so far, super! On the other hand if not, don't let it bother you. Seppukku is an acceptable option. (Unless your designation says "Project Manager", in which case you ought to be ashamed!)

All you need to know is we're goin to be scraping numbers off these spreadsheets to generate other numbers that are interesting, insightful and irresistible.

Onward, into the code!

All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1

The Lazy Moose - Part 01 of 03

When Laziness is a Virtue - Part 01 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl.

There are times when laziness is a virtue. There are places where when hubris is laudable. The time has come and let me show you the place.

Remember attributes? This whole piece centers around two attributes - lazy and default. Specifically, lazy-loading - which is when data magically appears when requested, and not until then. The default attribute supplies the magic wand that produces data.

Why is this useful? Suppose one has a chain of events arranged whereby a few high-level events recruit many middle-level events which in turn recruit myriad low-level events in order to achieve a high-level objective. Sort of like feudal Japan where the peasantry bowed to Samurai (Warrior Class) and Samurai bowed to Daimyo (Knights) and Daimyo to bigger Daimyo (Lords) who all bowed to the Shogun (Dictator) - who sometimes put the dick in dictator but more often than not, was a highly skilled leader and administrator. Can you think of such a hierarchy in the computational world, where low-level functions harvest information from databases or web-services and supply them on-request to pre-processors which in turn pass up clean, formatted data to persnickety middle-level processing functions that successively distill value until something interesting and useful comes out at the very top?

Ordinarily, such an application is designed as a 'push' system where data is pushed from one level to the next. Since the system has no way of knowing what will be requested next, it can only push information upwards until pre-fabricated goods sit on the shelves waiting to be picked off - whether required immediately or not. An "all or nothing" approach.

That is not an enlightened way to go about it. Would it not be better if components could be fabricated on demand - only when required? A 'pull' system where, once fabricated, a component remains available for the next request. What's not available is filled-in only when a request comes in.

Come to think of it, this is not unlike a "Just In Time" system for computer applications. Watch, how laziness becomes virtue.

The example I have chosen is a pragmatic one of Earned Value Analysis - a technique deployed by professional project managers for Monitoring and Control. It consists of three entities:
EV - Earned Value, representing how much value is earned from effort expended to-date.
PV - Planned Value, representing how much value the project planners thought would be earned to-date.
AC - Actual Costs, representing how much money has actually been spent to-date to generate the EV.

This helps monitor the project in terms of -
- Schedule Performance Index (SPI) - how we doin on time (baby)?
- Cost Performance Index (CPI) - how we doin on cost (dude)?

And the rest of this fascinating story of time, costs, sexual escapades, intrigue and as many as three of the 7 deadly sins unfolds in Part 2.


All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1

Thursday, November 12, 2009

Observe this!

Ch. 2 from HFDP - Part 03 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl. I cover the examples from the Head First Design Patterns book, replicating Java implementation in Moose. You need to obtain a copy of the book, which is not hard to come by.

Don't mess with the Moose! (Notes so you don't.)

An object, as the old saying goes, has data and methods. Data are stuffed in various slots and, to make a long story short, Moose provides 'attributes' to qualify a slot. Qualify - how? Well, for starters, what if you want a 'read-only' slot? That is achieved by setting the 'is' attribute to 'ro' - thus ensuring that Moose generates a getter (or accessor) for the method and omits the setter (or mutator).

Attributes are one cool feature of the Moose. They are the muscle and sinew. And the case-studies below illustrate some of the power.

Case of the Trigger-Happy Moose

The 'trigger' is a subroutine reference set upon a slot, so that the subroutine is invoked whenever the slot is set. This can happen both during object construction or later by passing a new object to the attribute's accessor method. However, it is not called when a value is provided via a 'default' or 'builder'.

The Moose Defaults

You do not need a constructor for a Moose class. Instead, use the 'default' attibute set upon each slot. The 'default' attribute holds a subroutine reference, the return of which is stuffed into the slot.
For self-reference in 'default', use $_[0] in the sub. When the sub is executed, it is invoked as a method on the object.
And therein lies the rub! Remember, remember (the 5th of November) .. it is the returned object or value that is stuffed into the slot. Be sure that what the sub returns is indeed what you want stuffed in the slot! There's many a slip twixt cup and lip (or slot and sub) .. many a Moose has been waylaid trying to set the slot explicitly in the definition of the default sub. The object is, after all, a hashref and the slots merely hash-keys. That is not how 'default' is used. Just have the sub return what you need in the slot and you will come to no grief.

The Lazy Moose

The lazy and default options options are linked. In fact, you cannot have a lazy attribute unless it has a default (or a builder, but we'll cover that later). If you try to make an attribute lazy without a default, class creation will fail with an exception.
What lazy means is that the slot is stuffed by the 'default' only upon request via accessor and not until then. This feature is so powerful and ingenious - certain applications may simply be impossible without it. Take for example a classical data structure in computer science - the binary tree. Without lazy loading, it leads to an implementation that consumes all of the computer's available memory. Now you wouldn't want that to happen to you, would you?

http://search.cpan.org/~drolsky/Moose-0.88/lib/Moose/Cookbook/Basics/Recipe3.pod

To Cook a Moose


(With apologies to a certain gun-toting Governor of Alaska.)
The 'BUILD' method is called after an object is created.
There are several ways to use a BUILD method. One of the most common is to check that the object state is valid. While we can validate individual attributes through the use of types, we can't validate the state of a whole object that way.

Why did the Moose weaken her reference?

Attribute 'weak_ref' assigned 1 ensures that the reference is weakened when it is set. So this reference will be ignored by Garbage Cleaning when handling the referred object. A weak reference will not prevent the referred object from being cleaned by garbage collector.

The Moose Modifies Her Methods


Method Modifiers are a Moose feature that allows hooking into code. One can alter class behavior by hooking into code before, after and even around. Basically, that means using the qualifiers 'before', 'after' and 'around' respectively.
Basically, a snippet of code encapsulated in a sub is executed before the modified method. (Or after, or around.)
Invoking the method that a method modifier hooks into in the definition of the method modifier leads to deep recursion.
That may sound obvious until one starts hooking into accessor/mutator methods to impose constraints, for example. This is a common occurrence when a slot is a container - such as an array (array-ref) or a hash (hash-ref). Then, usage of a method modifier to constrain the size of the container (for example) leads to disaster. Because to constrain the size of the container, you will need to access the slot .. get it?

The following DOs and DON'Ts for the methods 'before', 'after' should help one stay out of trouble -
DO
use Method Modification to add behavior to methods that Moose generates, such as accessor/mutator methods.
DON'T
however attempt to pre-process the arguments passed to the method thus modified. That will have no effect - changes to the argument list made in the method modifier are discarded when the said modifier exits. Speaking of which, return values are simply ignored.
DO
use method modification to execute logical checks that don't make sense as type constraints.
DON'T
however abuse this notion by attempting to rectify the input arguments. The correct action when checks fail is to raise an exception. In other words, die. Die! Or in the immortal words of the dying Creedy in "V for Vendetta", "Why won't you die?!" (To which, the masked character called "V" replies "Beneath this mask, there is no flesh, Mr. Creedy. Beneath this mask, is an Idea. And Ideas are bulletproof.")

Roundabout Moose

And just when you though method modification makes sense ..

.. Moose cruelly exposes your naivete!

The method modifier 'around', despite appearances, is fundamentally different from 'before' and 'after' - with 'around', you can modify the arguments being passed to the original method. You can even call the orginal method - as a matter of fact, you are required to. Unless you don't want to, that is. In which case, you need not call the original method at all.

Last, but not the least, you can modify the return value with 'around'. That is, if you want to. Wakarimasu ka?

As my martial arts instructor says, "Try it! You'll know." And that's enough for one day.


All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1

Monday, November 9, 2009

Observe this! (Cont'd)

Ch. 2 from HFDP - Part 02 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl. I cover the examples from the Head First Design Patterns book, replicating Java implementation in Moose. You need to obtain a copy of the book, which is not hard to come by.

When I was at the University of Colorado, I spent a chunk of my life driving a trusty Toyota truck in the Rockies. In the winter, it was not unusual to encounter a snow-storm that dumped enough snow to block the mountain passes. In those climes during those climbs, one would benefit from tuning into an AM radio channel that broadcast information upon the weather and road conditions.

So, how about a display that plugs-in the Weather Station to show current Temperature, Pressure and Humidity? That's just what 'currentConditionsDisplay' does.


package currentConditionsDisplay;
use Moose;

extends 'Observer', 'DisplayElement';

has 'temperature' => (
is => 'rw',
isa => 'Value',
);

has 'humidity' => (
is => 'rw',
isa => 'Num',
);

has 'weatherData' => (
is => 'rw',
isa => 'WeatherData',
trigger => \®isterMe,
predicate => 'has_Subject',
clearer => 'clear_Subject',
);

sub registerMe {
my ($self, $weatherData) = @_;
$weatherData->_registerObserver($self) if $self->has_Subject;
};

after 'update' => sub {
my $self = shift;
my ($temperature, $pressure, $humidity) = @_;
$self->temperature($temperature);
$self->humidity($humidity);
$self->display;
};

after 'display' => sub {
my $self = shift;
print ">> NOW conditions >>";
printf " TEM <%10.2f>\n HUM <%10.2f>\n", $self->temperature, $self->humidity;
print "<< ----- ROGEROUT <<";
};


See how the 'Observer' and 'DisplayElement' roles are composed into this display? There can be any number of plug-in classes developed for the Weather Station API. This allows the 'Observer' role to be mixed-in a plug-in class, leaving the developers of that class free to pimp their ride.

Notice that the Observer holds a copy of (i.e. reference to) the Subject. The 'predicate' method 'has_Subject' checks that such a relationship exists. The 'clearer' method 'clear_Subject' severs it.

What about the 'trigger'? When the 'weatherData' slot is set, the trigger ensures that the display is registered with Weather Station as an observer by invoking the Weather Station's '_registerObserver' method. What is the significance of the underscore in '_registerObserver'? That indicates this is a private method, not exposed for public consumption.

Let's roll this out now.

First, a Weather Station as an object of 'WeatherData' class:

my $weatherData = WeatherData->new;

Add an observer:

my $myDisplay = currentConditionsDisplay->new(weatherData => $weatherData);

And Lights, Camera, Action!

$weatherData->setMeasurements(107, 64, 27.5);

Et voila! Gentlemen, we got ourselves a Weather Station!

Now how about one that does things a little differently? Shows the average of the last five readings, perhaps? Here we go - Gentlemen, Ladies and Larry - I give you: 'averageConditionsDisplay'.


package averageConditionsDisplay;
use Moose;

extends 'Observer', 'DisplayElement';

has 'temperature' => (
is => 'rw',
isa => 'ArrayRef[Value]',
default => sub { [] },
);

has 'pressure' => (
is => 'rw',
isa => 'ArrayRef[Num]',
default => sub { [] },
);

has 'humidity' => (
is => 'rw',
isa => 'ArrayRef[Num]',
default => sub { [] },
);

has 'weatherData' => (
is => 'rw',
isa => 'WeatherData',
trigger => \®isterMe,
predicate => 'has_Subject',
clearer => 'clear_Subject',
);

sub registerMe {
my ($self, $weatherData) = @_;
$weatherData->_registerObserver($self) if $self->has_Subject;
};

before 'update' => sub {
my $self = shift;
shift(@{$self->temperature}) if (scalar(@{$self->temperature}) == 5);
shift(@{$self->pressure}) if (scalar(@{$self->pressure}) == 5);
shift(@{$self->humidity}) if (scalar(@{$self->humidity}) == 5);
};

after 'update' => sub {
my $self = shift;
my ($temperature, $pressure, $humidity) = @_;
push @{$self->temperature} , $temperature;
push @{$self->humidity} , $humidity;
push @{$self->pressure} , $pressure;
$self->display;
};

after 'display' => sub {
my $self = shift;
my $tallies = $self->total_me;
print ">> MEAN conditions >>";
printf " TEM <%10.2f>\n PRE <%10.2f>\n HUM <%10.2f>\n",
$tallies->{temperature}/5,
$tallies->{pressure}/5,
$tallies->{humidity}/5;
print "<< ------ ROGEROUT <<";
};

sub total_me {
my $self = shift;
my %netValues = ();
my $index = 0;
foreach my $thisTemperature (@{$self->temperature}) {
$netValues{'temperature'} += $self->temperature->[$index];
$netValues{'pressure'} += $self->pressure->[$index];
$netValues{'humidity'} += $self->humidity->[$index];
$index++;
};
return \%netValues;
}

What does this do differently? Let's see..

Firstly, temperature, pressure, humidity are stored in containers - arrays. That's nice because it allows averaging over a moving window. How is this moving window achieved? Enter Method Modifiers - a Moose special! If you ask a passing Moose, he (or she) will tell you that a Method Modifier is a way of hooking into methods. On prodding further, the Moose may reveal that Method Modification is closely aligned with inheritance and enables behavior-modification so that a child class can develop specialized behavior to focus on a problem-solving task.

In the instance at hand, for example, the 'update' behavior is altered with 'before' and 'after' modifiers. There is nothing special about 'after' which, after all, only implements a definition left out in the abstract 'Observer' parent class. The usage of 'before' is interesting though, because this is where the size of the 'window' is controlled. Take a look and convince yourself.

Could we not simple have achieved this with Method Modifiers on the get/set methods of 'temperature', 'pressure' and 'humidity' slots? (These are 'temperature', 'pressure' and 'humidity' respectively.) One could define a 'before' method modifier upon each of these and check the size of the array, resetting it using 'shift' on the 'size equals 5' condition. Right?

Right?

Wrong!

Remember that Method Modifiers - 'before', 'after' - are called whenever the method is called. So if you call the method in its modifier, you end up with deep recursion. That makes nobody happy and the God-Father mad! Just kidding about the God-Father.

If this seems obvious - good! If not, work through it. This is important. The last part of this monograph in 3 parts upon the Observer pattern notes some handy Moose-isms, lest the novice Moose enthusiast be struck down with Moose-itis.



All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1