alphagov/smart-answers — pull request #884 by heathd (2014-05-14 15:47:27+00:00)
Title: Ability to draw automatic flow charts - step1

I'd like to be able to draw flow charts automatically from Smart answers. However it's not possible to do that at the moment because it's impossible to programmatically determine the next node from a given smart answer node without actually running the smart answer.

I want to change the way that the next_node is calculated to make this possible.

So instead of:

```ruby
value_question :how_old_are_you? do
next_node do |age|
if age.to_i > 18
:adult_passport
else
:child_passport
end
end
end
```

it would be defined like this:

```ruby
value_question :how_old_are_you? do
next_node_if(:adult_passport) { |age| age.to_i > 18 }
next_node(:child_passport)
end
```

The `next_node_if` block defines a conditional next node which will be followed if the block evaluates to true. The `next_node_if` blocks are evaluated sequentially so the first one which matches will be followed. In this case, the `next_node(:child_passport)` is always followed.

In a few rare cases it may be difficult to change the next_node calculation code, so I've added an alternative `permitted_next_nodes` function for declaring the permitted next nodes from a given node (I'd like to make it illegal to transition to an undeclared node, but that will have to happen after all the flows have been updated to this new style).

## Predicate functions

As well as passing a block to the `next_node_if` function, you can also pass a predicate function (lambda), e.g.

```ruby
next_node_if(:adult_passport, ->(response) { response.to_i > 18})
```

I've defined a number of helpful predicate functions:

**For checkbox nodes**
* `response_is_one_of(accepted_responses)` - true if ONE of the `accepted_responses` was checked
* `response_has_all_of(required_responses)` - true if ALL of the `required_responses` were checked

**For country nodes**
* `country_in(countries)`

**For all nodes**
* `responded_with(required_response)` - true if the response == `required_response`

## Intermediate calculations for next node

One long-standing issue with calculations is that they are evaluated AFTER the next_node decision is made, but often you would like to calculate some intermediate value used during the next_node decision.

Now you can do that:

```ruby
next_node_calculation :weekly_salary_in_pence do |weekly_salary|
response.to_i * 100
end

next_node_if(:eligible) { weekly_salary_in_pence < 10000 }
```

## Validation

Another common pattern in the `next_node` blocks was to have an initial validation. This can now be done using an explicit `validate` declaration:

```ruby
validate { |response| Date.parse(response) <= Date.today }
```

which will raise an `InvalidResponse` exception if the block evaluates to false.

You can optionally pass a symbol which will be used as the message key passed to the exception raised:

```ruby
validate(:error_perm_prop_house) { |r| ! r.include?('permission,property,social_housing') }
```

## Examples

this PR includes two example flows converted to use the new style `calculate-statutory-sick-pay.rb` and `check-uk-visa.rb`.