From 345989370f52e876e6be502dde6b5ffd541ae27c Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Fri, 6 Feb 2009 01:56:18 +0000 Subject: [PATCH] Renamed ResponseJSON branching methods --- .../Asset/Wobject/Survey/ResponseJSON.pm | 88 ++++++++++++------- t/Asset/Wobject/Survey/ResponseJSON.t | 62 ++++++------- 2 files changed, 87 insertions(+), 63 deletions(-) diff --git a/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm b/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm index ae17315db..3581c92ca 100644 --- a/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm +++ b/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm @@ -37,11 +37,21 @@ number of questions answered (L<"questionsAnswered">) and the Survey start time This package is not intended to be used by any other Asset in WebGUI. =head2 surveyOrder - -Many methods in this class operate on the surveyOrder property. -This data strucutre is a deep set of arrays, similar in structure to -L. +This data strucutre is an array of Survey addresses (see +L), stored in the order +in which items are presented to the user. + +By making use of L methods which expect address params as +arguments, you can access Section/Question/Answer items in order by iterating over surveyOrder. + +For example: + + # Access sections in order.. + for my $address (@{ $self->surveyOrder }) { + my $section = $self->survey->section( $address ); + # etc.. + } In general, the surveyOrder data structure looks like: @@ -133,12 +143,9 @@ sub new { =head2 createSurveyOrder Computers and stores the order of Sections, Questions and Aswers for this Survey. -The order is represented as an array of addresses -(see L), -and is stored in the L<"surveyOrder"> property. +See L<"surveyOrder">. Questions and Answers that are set to be randomized are shuffled into a random order. -The L<"surveyOrder"> property is useful for keeping a record of what the user was presented with. =cut @@ -563,8 +570,8 @@ sub recordResponses { $self->lastResponse( $self->lastResponse + @questions ); # Do any requested branching.. - $self->goto($goto) if ( defined $goto ); - $self->gotoExpression($gotoExpression) if ( defined $gotoExpression ); + $self->processGoto($goto) if ( defined $goto ); + $self->processGotoExpression($gotoExpression) if ( defined $gotoExpression ); } else { # Required responses were missing, so we don't let the Survey terminate @@ -580,38 +587,55 @@ sub recordResponses { #------------------------------------------------------------------- -=head2 goto ( $variable ) +=head2 processGoto ( $variable ) -Looks through all sections and questions for their variable key, in order. If the requested +Looks through all sections and questions for their variable key, in order. If the requested $variable matches a variable, then the lastResponse is set so that that section or question -is the next displayed. If more than one section or question matches, then the first is used. +is the next displayed. If more than one variable name matches, then the first is used. =head3 $variable -The variable to look for in all sections and questions. +A variable name to match against all section and question variable names. =cut -sub goto { +sub processGoto { my $self = shift; - my $goto = shift; - for ( my $i = 0; $i <= $#{ $self->surveyOrder() }; $i++ ) { - my $section = $self->survey->section( $self->surveyOrder()->[$i] ); - my $question = $self->survey->question( $self->surveyOrder()->[$i] ); - if ( ref $section eq 'HASH' and $section->{variable} eq $goto ) { - $self->lastResponse( $i - 1 ); + my ($goto) = validate_pos(@_, {type => SCALAR}); + + # Iterate over items in order.. + my $itemIndex = 0; + for my $address (@{ $self->surveyOrder }) { + + # Retreive the section and question for this address.. + my $section = $self->survey->section( $address ); + my $question = $self->survey->question( $address ); + + # See if our goto variable matches the section variable.. + if ( ref $section eq 'HASH' && $section->{variable} eq $goto ) { + + # Fudge lastReponse so that the next response item will be our matching item + $self->lastResponse( $itemIndex - 1 ); last; } - if ( ref $question eq 'HASH' and $question->{variable} eq $goto ) { - $self->lastResponse( $i - 1 ); + + # See if our goto variable matches the question variable.. + if ( ref $question eq 'HASH' && $question->{variable} eq $goto ) { + + # Fudge lastReponse so that the next response item will be our matching item + $self->lastResponse( $itemIndex - 1 ); last; } + + # Increment the item index counter + $itemIndex++; } -} ## end sub goto + return; +} #------------------------------------------------------------------- -=head2 gotoExpression ( $gotoExpression ) +=head2 processGotoExpression ( $gotoExpression ) =head3 $gotoExpression @@ -654,7 +678,7 @@ But for now those things can be done manually using the limited subset defined. =cut -sub gotoExpression { +sub processGotoExpression { my $self = shift; my $expression = shift; @@ -670,9 +694,9 @@ sub gotoExpression { } } - # Process gotoExpressions one after the other (first one that's true wins) + # Parse gotoExpressions one after the other (first one that's true wins) foreach my $line (split '\n', $expression) { - my $processed = $self->processGotoExpression($line, \%responses); + my $processed = $self->parseGotoExpression($line, \%responses); next unless $processed; @@ -682,7 +706,7 @@ sub gotoExpression { if ($result) { $self->session->log->debug("Truthy, goto [$processed->{target}]"); - $self->goto($processed->{target}); + $self->processGoto($processed->{target}); return $processed; } else { $self->session->log->debug("Falsy, not branching"); @@ -692,7 +716,7 @@ sub gotoExpression { return; } -=head2 processGotoExpression ( $expression, $responses) +=head2 parseGotoExpression( ( $expression, $responses) Parses a single gotoExpression. Returns undef if processing fails, or the following hashref if things work out well: @@ -722,12 +746,12 @@ the expression generate an error because our list of valid tokens doesn't includ =cut -sub processGotoExpression { +sub parseGotoExpression { my $self = shift; my $expression = shift; my $responses = shift; - $self->session->log->debug("Processing gotoExpression: $expression"); + $self->session->log->debug("Parsing gotoExpression: $expression"); # Valid gotoExpression tokens are.. my $tokens = qr{\s|[-0-9=!<>+*/.()]}; diff --git a/t/Asset/Wobject/Survey/ResponseJSON.t b/t/Asset/Wobject/Survey/ResponseJSON.t index 95fc463ea..ba1694682 100644 --- a/t/Asset/Wobject/Survey/ResponseJSON.t +++ b/t/Asset/Wobject/Survey/ResponseJSON.t @@ -307,13 +307,13 @@ $rJSON->survey->question([3,1])->{variable} = 'goto 3-0'; ##Intentional duplica $rJSON->survey->question([3,2])->{variable} = 'goto 3-2'; $rJSON->lastResponse(0); -$rJSON->goto('goto 80'); +$rJSON->processGoto('goto 80'); is($rJSON->lastResponse(), 0, 'goto: no change in lastResponse if the variable cannot be found'); -$rJSON->goto('goto 1'); +$rJSON->processGoto('goto 1'); is($rJSON->lastResponse(), 2, 'goto: works on existing section'); -$rJSON->goto('goto 0-1'); +$rJSON->processGoto('goto 0-1'); is($rJSON->lastResponse(), 0, 'goto: works on existing question'); -$rJSON->goto('goto 3-0'); +$rJSON->processGoto('goto 3-0'); is($rJSON->lastResponse(), 5, 'goto: finds first if there are duplicates'); #################################################### @@ -321,28 +321,28 @@ is($rJSON->lastResponse(), 5, 'goto: finds first if there are duplicates'); # processGotoExpression # #################################################### -is($rJSON->processGotoExpression(), +is($rJSON->parseGotoExpression(), undef, 'processGotoExpression undef with empty arguments'); -is($rJSON->processGotoExpression('blah-dee-blah-blah'), +is($rJSON->parseGotoExpression('blah-dee-blah-blah'), undef, '.. and undef with duff expression'); -is($rJSON->processGotoExpression(':'), +is($rJSON->parseGotoExpression(':'), undef, '.. and undef with missing target'); -is($rJSON->processGotoExpression('t1:'), +is($rJSON->parseGotoExpression('t1:'), undef, '.. and undef with missing expression'); -cmp_deeply($rJSON->processGotoExpression('t1: 1'), +cmp_deeply($rJSON->parseGotoExpression('t1: 1'), { target => 't1', expression => '1'}, 'works for simple numeric expression'); -cmp_deeply($rJSON->processGotoExpression('t1: 1 - 23 + 456 * (78 / 9.0)'), +cmp_deeply($rJSON->parseGotoExpression('t1: 1 - 23 + 456 * (78 / 9.0)'), { target => 't1', expression => '1 - 23 + 456 * (78 / 9.0)'}, 'works for expression using all algebraic tokens'); -is($rJSON->processGotoExpression('t1: 1 + &'), undef, '.. but disallows expression containing non-whitelisted token'); -cmp_deeply($rJSON->processGotoExpression('t1: 1 = 3'), +is($rJSON->parseGotoExpression('t1: 1 + &'), undef, '.. but disallows expression containing non-whitelisted token'); +cmp_deeply($rJSON->parseGotoExpression('t1: 1 = 3'), { target => 't1', expression => '1 == 3'}, 'converts single = to =='); -cmp_deeply($rJSON->processGotoExpression('t1: 1 != 3 <= 4 >= 5'), +cmp_deeply($rJSON->parseGotoExpression('t1: 1 != 3 <= 4 >= 5'), { target => 't1', expression => '1 != 3 <= 4 >= 5'}, q{..but doesn't mess with other ops containing =}); -cmp_deeply($rJSON->processGotoExpression('t1: q1 + q2 * q3 - 4', { q1 => 11, q2 => 22, q3 => 33}), +cmp_deeply($rJSON->parseGotoExpression('t1: q1 + q2 * q3 - 4', { q1 => 11, q2 => 22, q3 => 33}), { target => 't1', expression => '11 + 22 * 33 - 4'}, 'substitues q for value'); -cmp_deeply($rJSON->processGotoExpression('t1: a silly var name * 10 + another var name', { 'a silly var name' => 345, 'another var name' => 456}), +cmp_deeply($rJSON->parseGotoExpression('t1: a silly var name * 10 + another var name', { 'a silly var name' => 345, 'another var name' => 456}), { target => 't1', expression => '345 * 10 + 456'}, '..it even works for vars with spaces in their names'); -is($rJSON->processGotoExpression('t1: qX + 3', { q1 => '7'}), +is($rJSON->parseGotoExpression('t1: qX + 3', { q1 => '7'}), undef, q{..but doesn't like invalid var names}); #################################################### @@ -362,30 +362,30 @@ $rJSON->recordResponses({ '1-0-0' => 'First answer', '1-0-0comment' => 'Section 1, question 0, answer 0 comment', }); -is($rJSON->gotoExpression('blah-dee-blah-blah'), undef, 'invalid gotoExpression is false'); -ok($rJSON->gotoExpression('s0: s1q0 = 3'), '3 == 3 is true'); -ok(!$rJSON->gotoExpression('s0: s1q0 = 4'), '3 == 4 is false'); -ok($rJSON->gotoExpression('s0: s1q0 != 2'), '3 != 2 is true'); -ok(!$rJSON->gotoExpression('s0: s1q0 != 3'), '3 != 3 is false'); -ok($rJSON->gotoExpression('s0: s1q0 > 2'), '3 > 2 is true'); -ok($rJSON->gotoExpression('s0: s1q0 < 4'), '3 < 2 is true'); -ok(!$rJSON->gotoExpression('s0: s1q0 >= 4'), '3 >= 4 is false'); -ok(!$rJSON->gotoExpression('s0: s1q0 <= 2'), '3 >= 4 is false'); +is($rJSON->processGotoExpression('blah-dee-blah-blah'), undef, 'invalid gotoExpression is false'); +ok($rJSON->processGotoExpression('s0: s1q0 = 3'), '3 == 3 is true'); +ok(!$rJSON->processGotoExpression('s0: s1q0 = 4'), '3 == 4 is false'); +ok($rJSON->processGotoExpression('s0: s1q0 != 2'), '3 != 2 is true'); +ok(!$rJSON->processGotoExpression('s0: s1q0 != 3'), '3 != 3 is false'); +ok($rJSON->processGotoExpression('s0: s1q0 > 2'), '3 > 2 is true'); +ok($rJSON->processGotoExpression('s0: s1q0 < 4'), '3 < 2 is true'); +ok(!$rJSON->processGotoExpression('s0: s1q0 >= 4'), '3 >= 4 is false'); +ok(!$rJSON->processGotoExpression('s0: s1q0 <= 2'), '3 >= 4 is false'); -cmp_deeply($rJSON->gotoExpression(<<"END_EXPRESSION"), {target => 's2', expression => '3 == 3'}, 'first true expression wins'); +cmp_deeply($rJSON->processGotoExpression(<<"END_EXPRESSION"), {target => 's2', expression => '3 == 3'}, 'first true expression wins'); s0: s1q0 <= 2 s2: s1q0 = 3 END_EXPRESSION -ok(!$rJSON->gotoExpression(<<"END_EXPRESSION"), 'but multiple false expressions still false'); +ok(!$rJSON->processGotoExpression(<<"END_EXPRESSION"), 'but multiple false expressions still false'); s0: s1q0 <= 2 s2: s1q0 = 345 END_EXPRESSION -$rJSON->gotoExpression('s0: s1q0 = 3'); -is($rJSON->lastResponse(), -1, '.. lastResponse changed to -1 due to goto(s0)'); -$rJSON->gotoExpression('s2: s1q0 = 3'); -is($rJSON->lastResponse(), 4, '.. lastResponse changed to 4 due to goto(s2)'); +$rJSON->processGotoExpression('s0: s1q0 = 3'); +is($rJSON->lastResponse(), -1, '.. lastResponse changed to -1 due to processGoto(s0)'); +$rJSON->processGotoExpression('s2: s1q0 = 3'); +is($rJSON->lastResponse(), 4, '.. lastResponse changed to 4 due to processGoto(s2)'); $rJSON->responses({}); $rJSON->questionsAnswered(-1 * $rJSON->questionsAnswered);