Refactored ResponseJSON and SurveyJSON

Added Params::Validate to ResponseJSON.pm
Refactored ResponseJSON constructor and re-ordered params for
consistency
Added new ->session accessor
Updates tests
Removed unnecessary logging methods
Further refactored SurveyJSON, gave private variables underscores,
replaced direct hash access with accessors
This commit is contained in:
Patrick Donelan 2009-02-03 08:31:43 +00:00
parent 8833459c74
commit 821635eb71
5 changed files with 84 additions and 100 deletions

View file

@ -1039,7 +1039,7 @@ sub loadResponseJSON {
if ( !defined $jsonHash );
$self->{response}
= WebGUI::Asset::Wobject::Survey::ResponseJSON->new( $jsonHash, $self->session->errorHandler, $self->survey );
= WebGUI::Asset::Wobject::Survey::ResponseJSON->new( $self->survey, $jsonHash );
} ## end sub loadResponseJSON
#-------------------------------------------------------------------

View file

@ -20,60 +20,63 @@ Package WebGUI::Asset::Wobject::Survey::ResponseJSON
Helper class for WebGUI::Asset::Wobject::Survey. It manages data
from the user, sets the order of questions and answers in the survey,
based on forks, and gotos, and also handles expiring the survey
based on branches, and gotos, and also handles expiring the survey
due to time limits.
This package is not intended to be used by any other Asset in WebGUI.
=cut
use strict;
use JSON;
use Data::Dumper;
use Params::Validate qw(:all);
Params::Validate::validation_options( on_fail => sub { WebGUI::Error::InvalidParam->throw( error => shift ) } );
#-------------------------------------------------------------------
=head2 new ( $json, $log, $survey )
=head2 new ( $survey, $json )
Object constructor.
=head3 $json
Pass in some JSON to be serialized into a data structure. Useful JSON would
contain a hash with "startTime", "surveyOrder", "responses", "lastReponse"
and "questionsAnswered" keys, with appropriate values.
=head3 $log
The session logger, from $session->log. The class needs nothing else from the
session object.
=head3 $survey
A WebGUI::Asset::Wobject::Survey::SurveyJSON object that represents the current
survey.
=head3 $json
A JSON string used to construct a new Perl object. The string should represent
a JSON hash made up of "startTime", "surveyOrder", "responses", "lastReponse"
and "questionsAnswered" keys, with appropriate values.
=cut
sub new {
my $class = shift;
my $json = shift;
my $log = shift;
my $survey = shift;
my $temp = from_json($json) if defined $json;
my $self = defined $temp ? $temp : {};
$self->{survey} = $survey;
$self->{log} = $log;
$self->{responses} = defined $temp->{responses} ? $temp->{responses} : {};
$self->{lastResponse} = defined $temp->{lastResponse} ? $temp->{lastResponse} : -1;
$self->{questionsAnswered} = defined $temp->{questionsAnswered} ? $temp->{questionsAnswered} : 0;
$self->{startTime} = defined $temp->{startTime} ? $temp->{startTime} : time();
#an array of question addresses, with the third member being an array of answers
$self->{surveyOrder} = defined $temp->{surveyOrder} ? $temp->{surveyOrder} : [];
bless( $self, $class );
return $self;
} ## end sub new
my ($survey, $json) = validate_pos(@_, {isa => 'WebGUI::Asset::Wobject::Survey::SurveyJSON' }, { type => SCALAR, optional => 1});
# Load json object if given..
my $jsonData = $json ? from_json($json) : {};
# Create skeleton object..
my $self = {
# First define core members..
_survey => $survey,
_session => $survey->session,
# And now object defaults..
responses => {},
lastResponse => -1,
questionsAnswered => 0,
startTime => time(),
surveyOrder => [],
# And finally, allow jsonData to override defaults and/or add other members
%$jsonData,
};
return bless( $self, $class );
}
#----------------------------------------------------------------------------
@ -125,6 +128,19 @@ sub createSurveyOrder {
#-------------------------------------------------------------------
=head2 session
Accessor method for the local WebGUI::Session reference
=cut
sub session {
my $self = shift;
return $self->{_session};
}
#-------------------------------------------------------------------
=head2 shuffle ( @array )
Returns the contents of @array in a random order.
@ -151,8 +167,8 @@ Serializes the object to JSON, after deleting the log and survey objects stored
sub freeze {
my $self = shift;
my %temp = %{$self};
delete $temp{log};
delete $temp{survey};
delete $temp{_session};
delete $temp{_survey};
return to_json( \%temp );
}
@ -573,14 +589,14 @@ sub gotoExpression {
# (ab)use perl's eval to evaluate the processed expression
my $result = eval "$processed->{expression}";
$self->warn($@) if $@;
$self->session->log->warn($@) if $@;
if ($result) {
$self->debug("Truthy, goto [$processed->{target}]");
$self->session->log->debug("Truthy, goto [$processed->{target}]");
$self->goto($processed->{target});
return $processed;
} else {
$self->debug("Falsy, not branching");
$self->session->log->debug("Falsy, not branching");
next;
}
}
@ -622,22 +638,22 @@ sub processGotoExpression {
my $expression = shift;
my $responses = shift;
$self->debug("Processing gotoExpression: $expression");
$self->session->log->debug("Processing gotoExpression: $expression");
# Valid gotoExpression tokens are..
my $tokens = qr{\s|[-0-9=!<>+*/.()]};
my ( $target, $rest ) = $expression =~ /\s* ([^:]+?) \s* : \s* (.*)/x;
$self->debug("Parsed as Target: [$target], Expression: [$rest]");
$self->session->log->debug("Parsed as Target: [$target], Expression: [$rest]");
if ( !defined $target ) {
$self->warn('Target undefined');
$self->session->log->warn('Target undefined');
return;
}
if ( !defined $rest || $rest eq '' ) {
$self->warn('Expression undefined');
$self->session->log->warn('Expression undefined');
return;
}
@ -650,11 +666,11 @@ sub processGotoExpression {
$rest =~ s/(?<![!<>])=(?!=)/==/g;
if ( $rest !~ /^$tokens+$/ ) {
$self->warn("Contains invalid tokens: $rest");
$self->session->log->warn("Contains invalid tokens: $rest");
return;
}
$self->debug("Processed as: $rest");
$self->session->log->debug("Processed as: $rest");
return {
target => $target,
@ -842,35 +858,7 @@ Note, this is an unsafe reference.
sub survey {
my $self = shift;
return $self->{survey};
return $self->{_survey};
}
#-------------------------------------------------------------------
=head2 log
Logs an error to the webgui log file, using the session logger.
=cut
sub log {
my ( $self, $message ) = @_;
if ( defined $self->{log} ) {
$self->{log}->debug($message);
}
}
sub debug {
my ( $self, $message) = @_;
if ( defined $self->{log} ) {
$self->{log}->debug($message);
}
}
sub warn {
my ( $self, $message) = @_;
if ( defined $self->{log} ) {
$self->{log}->warn($message);
}
}
1;

View file

@ -78,20 +78,16 @@ sub new {
my $class = shift;
my ($session, $json) = validate_pos(@_, {isa => 'WebGUI::Session' }, { type => SCALAR, optional => 1});
# Load json object if given..
my $jsonData = $json ? from_json($json) : {};
# Create skeleton object..
my $self = {
session => $session,
sections => [],
survey => {},
_session => $session,
_sections => $jsonData->{sections} || [],
_survey => $jsonData->{survey} || {},
};
# Load json object if given..
if ($json) {
my $decoded_json = from_json($json);
$self->{sections} = $decoded_json->{sections} if defined $decoded_json->{sections};
$self->{survey} = $decoded_json->{survey} if defined $decoded_json->{survey};
}
bless( $self, $class );
# Initialise the survey data structure if empty..
@ -111,8 +107,8 @@ components of this object.
sub freeze {
my $self = shift;
return to_json(
{ sections => $self->{sections},
survey => $self->{survey},
{ sections => $self->sections,
survey => $self->{_survey},
}
);
}
@ -293,13 +289,13 @@ sub getObject {
return unless $count;
if ( $count == 1 ) {
return dclone $self->{sections}->[ sIndex($address) ];
return dclone $self->sections->[ sIndex($address) ];
}
elsif ( $count == 2 ) {
return dclone $self->{sections}->[ sIndex($address) ]->{questions}->[ qIndex($address) ];
return dclone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ];
}
else {
return dclone $self->{sections}->[ sIndex($address) ]->{questions}->[ qIndex($address) ]->{answers}
return dclone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ]->{answers}
->[ aIndex($address) ];
}
}
@ -770,7 +766,7 @@ sub remove {
if ( $count == 1 ) {
# Make sure the first section isn't removed unless we REALLY want to
if ( sIndex($address) != 0 || defined $movingOverride ) {
splice( @{ $self->{sections} }, sIndex($address), 1 );
splice( @{ $self->sections }, sIndex($address), 1 );
}
}
elsif ( $count == 2 ) {
@ -1077,7 +1073,7 @@ Returns a reference to all the sections in this object.
sub sections {
my $self = shift;
return $self->{sections};
return $self->{_sections};
}
=head2 totalSections
@ -1157,7 +1153,7 @@ sub section {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF});
return $self->{sections}->[ $address->[0] ];
return $self->sections->[ $address->[0] ];
}
=head2 session
@ -1168,7 +1164,7 @@ Accessor method for the local WebGUI::Session reference
sub session {
my $self = shift;
return $self->{session};
return $self->{_session};
}
=head2 questions ($address)
@ -1185,7 +1181,7 @@ sub questions {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF});
return $self->{sections}->[ $address->[0] ]->{questions};
return $self->sections->[ $address->[0] ]->{questions};
}
=head2 question ($address)
@ -1202,7 +1198,7 @@ sub question {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF});
return $self->{sections}->[ $address->[0] ]->{questions}->[ $address->[1] ];
return $self->sections->[ $address->[0] ]->{questions}->[ $address->[1] ];
}
=head2 answers ($address)
@ -1219,7 +1215,7 @@ sub answers {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF});
return $self->{sections}->[ $address->[0] ]->{questions}->[ $address->[1] ]->{answers};
return $self->sections->[ $address->[0] ]->{questions}->[ $address->[1] ]->{answers};
}
=head2 answer ($address)
@ -1236,7 +1232,7 @@ sub answer {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF});
return $self->{sections}->[ $address->[0] ]->{questions}->[ $address->[1] ]->{answers}->[ $address->[2] ];
return $self->sections->[ $address->[0] ]->{questions}->[ $address->[1] ]->{answers}->[ $address->[2] ];
}
=head2 sIndex ($address)

View file

@ -40,7 +40,7 @@ skip $tests, "Unable to load ResponseJSON" unless $usedOk;
####################################################
my $newTime = time();
$responseJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new('{}', $session->log);
$responseJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(buildSurveyJSON($session), '{}');
isa_ok($responseJSON , 'WebGUI::Asset::Wobject::Survey::ResponseJSON');
is($responseJSON->lastResponse(), -1, 'new: default lastResponse is -1');
@ -50,7 +50,7 @@ is_deeply( $responseJSON->responses, {}, 'new: by default, responses is an empty
is_deeply( $responseJSON->surveyOrder, [], 'new: by default, responses is an empty arrayref');
my $now = time();
my $rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(qq!{ "startTime": $now }!, $session->log);
my $rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(buildSurveyJSON($session), qq!{ "startTime": $now }!);
cmp_ok(abs($rJSON->startTime() - $now), '<=', 2, 'new: startTime set using JSON');
####################################################
@ -85,7 +85,7 @@ ok( ! $rJSON->hasTimedOut(4*60), 'hasTimedOut, limit check');
#
####################################################
$rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(q!{}!, $session->log, buildSurveyJSON($session));
$rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(buildSurveyJSON($session), q!{}!);
$rJSON->createSurveyOrder();
cmp_deeply(
@ -125,7 +125,7 @@ cmp_deeply(
{
no strict "refs";
no warnings;
my $rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(q!{}!, $session->log, buildSurveyJSON($session));
my $rJSON = WebGUI::Asset::Wobject::Survey::ResponseJSON->new(buildSurveyJSON($session), q!{}!);
$rJSON->survey->section([0])->{randomizeQuestions} = 0;
my $shuffleName = "WebGUI::Asset::Wobject::Survey::ResponseJSON::shuffle";
my $shuffleCalled = 0;

View file

@ -2090,7 +2090,7 @@ isa_ok($surveyJSON->session, 'WebGUI::Session', 'session() accessor works');
sub summarizeSectionSkeleton {
my ($skeleton) = @_;
my $summary = [];
foreach my $section (@{ $skeleton->{sections} }) {
foreach my $section (@{ $skeleton->{_sections} }) {
my $summarySection = {
title => $section->{title},
questions => [],