|
1 | 1 | # Decision Tree |
2 | 2 |
|
| 3 | +[](https://buildkite.com/jobready/decision-tree) |
| 4 | + |
3 | 5 | Decision Tree is an easy way of defining rules/workflows that progress an |
4 | 6 | object's state through a series of boolean decisions. |
5 | 7 |
|
@@ -90,6 +92,73 @@ class Change < ActiveRecord::Base |
90 | 92 | end |
91 | 93 | ``` |
92 | 94 |
|
| 95 | +##Finishing the Workflow |
| 96 | +When a workflow has been completed, and you don't want it to evaluate again, you can call `finish!` on the workflow. |
| 97 | + |
| 98 | +```ruby |
| 99 | +class TestWorkflow < DecisionTree::Workflow |
| 100 | + def do_something |
| 101 | + ... |
| 102 | + end |
| 103 | + |
| 104 | + start do |
| 105 | + do_something |
| 106 | + end |
| 107 | + |
| 108 | + decision :do_something do |
| 109 | + yes { finish! } |
| 110 | + no { finish! } |
| 111 | + end |
| 112 | +end |
| 113 | +``` |
| 114 | + |
| 115 | +This will mark the workflow as finished in the state cache, and call `store_steps!` on the carrier, passing the list of `DecisionTree::Step` objects that were taken on the path to finishing the workflow. After it is finished, subsequent invocations of the workflow will no longer execute the workflow, but call `fetch_steps` on the state carrier. |
| 116 | + |
| 117 | +## Storing Steps |
| 118 | +Implementation of the storage of the steps is left to the application, and does not need to be implemented. DecisionTree will still work correctly without the step storage, but you will be unable to retrieve/display the steps when reloading the workflow after completion. If this is satisfactory, you can leave `store_steps!` and `fetch_steps` empty. |
| 119 | + |
| 120 | +If you do need to store the final steps, below is an example implementation: |
| 121 | + |
| 122 | +```ruby |
| 123 | +class Change < ActiveRecord::Base |
| 124 | + has_many :workflow_steps, as: :workflowable |
| 125 | + |
| 126 | + def store_steps!(steps) |
| 127 | + workflow_steps.destroy_all |
| 128 | + |
| 129 | + steps.each_with_index do |step, position| |
| 130 | + new_step = WorkflowStep.from_decision_step(step, self, position) |
| 131 | + new_step.save! |
| 132 | + end |
| 133 | + end |
| 134 | + |
| 135 | + def fetch_steps |
| 136 | + workflow_steps |
| 137 | + end |
| 138 | +end |
| 139 | + |
| 140 | +class WorkflowStep < ActiveRecord::Base |
| 141 | + belongs_to :workflowable, polymorphic: true |
| 142 | + default_scope { order(position: :asc) } |
| 143 | + |
| 144 | + delegate :display, to: :decision_step, allow_nil: true |
| 145 | + def decision_step |
| 146 | + @step ||= DecisionTree::Step.new(step_type, step_info) |
| 147 | + end |
| 148 | + |
| 149 | + def self.from_decision_step(decision_step, parent, position) |
| 150 | + step_attrs = { |
| 151 | + step_type: decision_step.step_type, |
| 152 | + step_info: decision_step.step_info, |
| 153 | + workflowable: parent, |
| 154 | + position: position |
| 155 | + } |
| 156 | + self.new(step_attrs) |
| 157 | + end |
| 158 | +end |
| 159 | +``` |
| 160 | + |
| 161 | + |
93 | 162 | ##Displaying the Workflow |
94 | 163 | Human readable display of workflow steps can be achieved by using `DecisionTree::Step#display`. |
95 | 164 | E.g. |
@@ -123,7 +192,7 @@ Any idempotent calls (identified by a trailing `!`) are defined under `idempoten |
123 | 192 | Any regular steps (with a yes/no outcome) are defined directly under `workflow_steps`, and contain values for both 'yes', and 'no'. Note that these keys should be defined as strings (explicitly wrapped in quotes), otherwise YAML helpfully converts these to booleans, which will not be matched when looking for a description. |
124 | 193 |
|
125 | 194 | ### Implicit Display Values |
126 | | -Semi-friendly display values are still returned if no translation has been defined. |
| 195 | +Semi-friendly display values are still returned if no translation has been defined. |
127 | 196 | For a regular step, the question will be rendered, followed by the answer. |
128 | 197 |
|
129 | 198 | ``` |
|
0 commit comments