easy_flow 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/models/easy_flow/compare_two_values.rb +1 -1
- data/app/models/easy_flow/progress/kept.rb +1 -0
- data/app/models/easy_flow/settings.rb +3 -2
- data/app/models/easy_flow/step_type/declaration.rb +4 -2
- data/app/models/easy_flow/validator.rb +17 -2
- data/app/models/easy_flow/wait.rb +1 -1
- data/lib/easy_flow/version.rb +1 -1
- data/the_local/agents/easy_flow-develop.md +27 -18
- data/the_local/agents/easy_flow-info.md +9 -8
- data/the_local/agents/easy_flow-install.md +4 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 870800005299f5ce55bcd2c9dc3ec1a51ed5ba05625a5346e20fb6136bad0193
|
|
4
|
+
data.tar.gz: f486029e0d05ed91a24e8b6acd7d88e747be723194b0d2ce730dace7fb8d99c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ccee7f56367527139ddec2e36d79b810c72b7001e9902c242741335fcd167198c316c66cd90cef7ff1231a4a99e8593393847df6afb8e30b34bfccfd97e0513
|
|
7
|
+
data.tar.gz: 689f358c686509cb1dc2ef852518c52f4f28754f921e0a6636d295823da100d84ca2704700d17b76a07bdad5c333b14c3f9e3e3fe0440f57f2b04d04fad06bed
|
|
@@ -9,7 +9,7 @@ module EasyFlow
|
|
|
9
9
|
setting :step, type: :previous_step
|
|
10
10
|
setting :output, outputs_of: :step
|
|
11
11
|
setting :comparison, type: :select, options: COMPARISONS.keys, required: true
|
|
12
|
-
setting :other_step, type: :previous_step
|
|
12
|
+
setting :other_step, type: :previous_step, optional: true
|
|
13
13
|
setting :other_output, outputs_of: :other_step
|
|
14
14
|
setting :amount, type: :float
|
|
15
15
|
|
|
@@ -3,7 +3,7 @@ module EasyFlow
|
|
|
3
3
|
attr_reader :fields, :labels, :record_fields, :record_labels, :record_choices, :record_outputs_of, :record_required, :drawn_from, :outputs_of
|
|
4
4
|
|
|
5
5
|
def initialize(fields: {}, labels: {}, record_fields: {}, record_labels: {}, record_choices: {}, record_outputs_of: {}, record_required: {}, choices: {},
|
|
6
|
-
limits: {}, checks: {}, required: [], drawn_from: {}, outputs_of: {}, kept: {})
|
|
6
|
+
limits: {}, checks: {}, required: [], optional: [], drawn_from: {}, outputs_of: {}, kept: {})
|
|
7
7
|
@fields = fields
|
|
8
8
|
@labels = labels
|
|
9
9
|
@record_fields = record_fields
|
|
@@ -15,6 +15,7 @@ module EasyFlow
|
|
|
15
15
|
@limits = limits
|
|
16
16
|
@checks = checks
|
|
17
17
|
@required = required
|
|
18
|
+
@optional = optional
|
|
18
19
|
@drawn_from = drawn_from
|
|
19
20
|
@outputs_of = outputs_of
|
|
20
21
|
@kept = kept
|
|
@@ -51,7 +52,7 @@ module EasyFlow
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def required
|
|
54
|
-
fields.keys.select { |name| @required.include?(name) || naming_steps.include?(name) }
|
|
55
|
+
fields.keys.select { |name| @required.include?(name) || (naming_steps.include?(name) && !@optional.include?(name)) }
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
def requirements_for(config)
|
|
@@ -19,6 +19,7 @@ module EasyFlow
|
|
|
19
19
|
@kept = {}
|
|
20
20
|
@declared_outputs = []
|
|
21
21
|
@required = []
|
|
22
|
+
@optional = []
|
|
22
23
|
@awaits_input = false
|
|
23
24
|
@ends_here = false
|
|
24
25
|
@begins_here = false
|
|
@@ -94,7 +95,7 @@ module EasyFlow
|
|
|
94
95
|
@naming = naming
|
|
95
96
|
end
|
|
96
97
|
|
|
97
|
-
def setting(name, type: nil, from: nil, outputs_of: nil, label: nil, options: nil, limit: nil, check: nil, required: false, kept_on: nil, attribute: nil, &entries)
|
|
98
|
+
def setting(name, type: nil, from: nil, outputs_of: nil, label: nil, options: nil, limit: nil, check: nil, required: false, optional: false, kept_on: nil, attribute: nil, &entries)
|
|
98
99
|
type ||= :from_step if from
|
|
99
100
|
type ||= :from_step if outputs_of
|
|
100
101
|
raise UnknownFieldType, "#{type} is not one of #{FIELD_TYPES.join(', ')}" unless FIELD_TYPES.include?(type)
|
|
@@ -103,6 +104,7 @@ module EasyFlow
|
|
|
103
104
|
@outputs_of[name] = outputs_of if outputs_of
|
|
104
105
|
@kept[name] = { finder: kept_on, attribute: attribute || name } if kept_on
|
|
105
106
|
@required += [ name ] if required
|
|
107
|
+
@optional += [ name ] if optional
|
|
106
108
|
|
|
107
109
|
declare_entries(name, entries) if type == :list
|
|
108
110
|
declare_choices(name, type, options)
|
|
@@ -146,7 +148,7 @@ module EasyFlow
|
|
|
146
148
|
def settings
|
|
147
149
|
Settings.new(fields: @fields, labels: @labels, record_fields: @record_fields,
|
|
148
150
|
record_labels: @record_labels, record_choices: @record_choices, record_outputs_of: @record_outputs_of, record_required: @record_required, choices: @choices, limits: @limits, checks: @checks,
|
|
149
|
-
required: @required, drawn_from: @drawn_from, outputs_of: @outputs_of, kept: @kept)
|
|
151
|
+
required: @required, optional: @optional, drawn_from: @drawn_from, outputs_of: @outputs_of, kept: @kept)
|
|
150
152
|
end
|
|
151
153
|
end
|
|
152
154
|
end
|
|
@@ -176,16 +176,31 @@ module EasyFlow
|
|
|
176
176
|
def missing_settings
|
|
177
177
|
@document.nodes.flat_map do |node|
|
|
178
178
|
required_of(node).reject { |name| node.config[name.to_s].present? }
|
|
179
|
-
.map { |name| Violation.new(node: node.id, problem: :missing_setting, detail: name.to_s) } + missing_entry_settings(node)
|
|
179
|
+
.map { |name| Violation.new(node: node.id, problem: :missing_setting, detail: name.to_s) } + missing_entry_settings(node) + unchosen_outputs(node)
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
+
def settings_of(node)
|
|
184
|
+
@registry.fetch(node.type).settings
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def unchosen_outputs(node)
|
|
188
|
+
return [] unless @registry.registered?(node.type)
|
|
189
|
+
|
|
190
|
+
settings = @registry.fetch(node.type).settings
|
|
191
|
+
settings.outputs_of.reject { |output, _source| settings.required.include?(output) }
|
|
192
|
+
.select { |output, source| node.config[source.to_s].present? && node.config[output.to_s].blank? }
|
|
193
|
+
.map { |output, _source| Violation.new(node: node.id, problem: :missing_setting, detail: output.to_s) }
|
|
194
|
+
end
|
|
195
|
+
|
|
183
196
|
def missing_entry_settings(node)
|
|
184
197
|
return [] unless @registry.registered?(node.type)
|
|
185
198
|
|
|
186
199
|
@registry.fetch(node.type).settings.record_required.flat_map do |list, required|
|
|
200
|
+
outputs = settings_of(node).record_outputs_of.fetch(list, {})
|
|
187
201
|
Array(node.config[list.to_s]).each_with_index.flat_map do |entry, index|
|
|
188
|
-
|
|
202
|
+
unchosen = outputs.select { |output, source| entry.to_h[source.to_s].present? && entry.to_h[output.to_s].blank? }.keys
|
|
203
|
+
(required.reject { |name| entry.to_h[name.to_s].present? } + unchosen)
|
|
189
204
|
.map { |name| Violation.new(node: node.id, problem: :missing_setting, detail: "#{list} #{index + 1} #{name}") }
|
|
190
205
|
end
|
|
191
206
|
end
|
|
@@ -16,7 +16,7 @@ module EasyFlow
|
|
|
16
16
|
def self.named(config)
|
|
17
17
|
return config["date"] if config["date"].present?
|
|
18
18
|
|
|
19
|
-
waited = config["time_of_day"].presence || ("#{config["minutes"]} minutes" if config["minutes"].present?)
|
|
19
|
+
waited = config["time_of_day"].presence || ("#{config["minutes"]} #{"minute".pluralize(config["minutes"].to_i)}" if config["minutes"].present?)
|
|
20
20
|
"#{waited} after #{config["step"]}" if waited
|
|
21
21
|
end
|
|
22
22
|
|
data/lib/easy_flow/version.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: easy_flow-develop
|
|
3
|
-
description: Use PROACTIVELY for adding a step type to easy_flow flows (a step that asks the visitor something, takes several answers at once, computes a value from earlier answers, records several values under names an admin enters in the step's settings, counts how many times an earlier step was answered so a loop can give up after a number of tries, picks the next branch, or holds the visitor until something outside the flow has happened), branching on whether all or any of several comparisons of earlier answers hold, holding a run until a number of minutes after an earlier answer, the next time of day after it, or a date, branching on the hour, weekday or time an earlier step was completed or on the minutes between two completed steps, reading when each step was completed and when a waiting run will move on, showing a step differently depending on the visitor's stored run, branching on what a visitor ticked on a checklist, moving a paused run on once what it waits for has happened, refusing a blank or invalid answer to a step with a message, offering an admin a step setting whose options are read from the app's own records, letting an admin pick another flow of the same host in a step's settings, adding a step type that starts another flow and works out which flow and version to run from its own settings, adding a step type that starts another flow chosen from the visitor's run, such as its owner or its answers, offering a visitor only the answers to a step that their earlier answers allow, filling in a step that has only one possible answer without asking it, skipping a step that does not apply to a visitor, letting an admin edit a value stored on one of the app's own records from a step's settings on the canvas, serving a host's flows from the app's own controller and routes, starting a visitor's run on a published version of a flow other than the live one, acting when a visitor finishes a flow, acting on the output a visitor's flow ended with, telling whether a run ended at an End step or was stopped by a loop that came back with nothing new answered and at which step, keeping a flow from being published while a step that starts another flow names a flow or version that cannot run or leads back to the flow it sits in, finding the run of another flow that a Flow step started, the run a waiting run is waiting on, and the run that started it, sending a visitor back to the run that started a flow once that flow ends, handling a run whose starting run was removed with its flow, handling a Flow step that cannot start its flow
|
|
3
|
+
description: Use PROACTIVELY for adding a step type to easy_flow flows (a step that asks the visitor something, takes several answers at once, computes a value from earlier answers, records several values under names an admin enters in the step's settings, counts how many times an earlier step was answered so a loop can give up after a number of tries, picks the next branch, or holds the visitor until something outside the flow has happened), branching on whether all or any of several comparisons of earlier answers hold, holding a run until a number of minutes after an earlier answer, the next time of day after it, or a date, branching on the hour, weekday or time an earlier step was completed or on the minutes between two completed steps, reading when each step was completed and when a waiting run will move on, showing a step differently depending on the visitor's stored run, branching on what a visitor ticked on a checklist, moving a paused run on once what it waits for has happened, refusing a blank or invalid answer to a step with a message, offering an admin a step setting whose options are read from the app's own records, letting an admin pick another flow of the same host in a step's settings, adding a step type that starts another flow and works out which flow and version to run from its own settings, adding a step type that starts another flow chosen from the visitor's run, such as its owner or its answers, offering a visitor only the answers to a step that their earlier answers allow, filling in a step that has only one possible answer without asking it, skipping a step that does not apply to a visitor, letting an admin edit a value stored on one of the app's own records from a step's settings on the canvas, serving a host's flows from the app's own controller and routes, starting a visitor's run on a published version of a flow other than the live one, acting when a visitor finishes a flow, acting on the output a visitor's flow ended with, telling whether a run ended at an End step or was stopped by a loop that came back with nothing new answered and at which step, keeping a flow from being published while a step that starts another flow names a flow or version that cannot run or leads back to the flow it sits in, finding the run of another flow that a Flow step started, the run a waiting run is waiting on, and the run that started it, sending a visitor back to the run that started a flow once that flow ends, handling a run whose starting run was removed with its flow, handling a Flow step that cannot start its flow, whose flow ended with no output, or whose flow was stopped by a loop, taking one file or several from a visitor at a step, showing a step's recorded answer by a label the step type works out, and reading a run's recorded answers and question labels, including every answer ticked on a checklist and every visit's answer when a flow loops back to a question already asked — MUST BE USED instead of hand-rolling questionnaire steps, checkbox lists, file upload steps, branching logic, answer lists filtered by earlier answers, skipped questions, number comparisons, comparisons of two earlier answers, and/or conditions over several comparisons, hard-coded setting options, copying a record's value into a flow and back, answer validation, retry counters, timers or scheduled delays, step completion timestamps, polling or "come back later" pages, flow controllers or answer lookups.
|
|
4
4
|
tools: Read, Write, Edit, Grep
|
|
5
5
|
scope: guided flows — versioned documents of steps and the connections between them, drawn on a canvas by an admin and run by a visitor one step at a time, with step types the host registers
|
|
6
6
|
---
|
|
@@ -9,7 +9,7 @@ This local follows the steps below exactly and invents none. Where a step names
|
|
|
9
9
|
|
|
10
10
|
## What easy_flow is
|
|
11
11
|
|
|
12
|
-
A Rails engine for flows an admin draws on a canvas and a visitor runs one step at a time. Each step is an instance of a step type. The engine ships thirteen: start (`:start`), end (`:terminal`, whose one setting is the output an admin writes for a flow that ends there), question (`:question`, a question with a list of answers of which the visitor picks one, which the admin can mark required so a blank answer is refused), checklist (`:checklist`, a question with a list of answers of which the visitor ticks any number, which the admin can mark required so a visitor who ticks nothing is refused), File upload (`:file_upload`, a question the visitor answers by choosing one file from a file upload control labelled with the question text, with an admin setting listing the accepted file types as comma-separated extensions such as `.pdf, .png`, which limits the visitor's file picker to those types and is shown under the control as `Accepts` followed by the list as the admin wrote it, such as `Accepts .pdf, .png`, which the admin can mark required so a visitor who chooses no file is refused, and whose recorded answer is the reference the host's file store returned for the file), five that pick a branch from earlier answers with no code — condition (`:condition`, is or is not a chosen value, where the admin must pick one of the earlier step's outputs and the answer is read from that output when the earlier step recorded several and whole otherwise, so a Condition cannot be completed on a step whose type declares no output), switch (`:switch`, follows the connection labelled with the answer), compare (`:compare`, reads the answer as a number, from the output the admin picks when the earlier step recorded several, and checks it by more than, less than, at least or at most against an amount) and Compare two values (`:compare_two_values`, reads the answers of two earlier steps the admin picks as numbers, reading each from the output the admin picks when that step recorded several, and checks the first by more than, less than, at least, at most or equal to against the second plus an amount the admin may give, a blank amount adding nothing, following its `true` or `false` connection, and following `false` when either answer is missing, is an array or is not a number) and All of or Any of (`:all_or_any`, holds a list of comparisons the admin enters and a `Must hold` setting of `all of` or `any of`, where each comparison asks for the same things as a Compare two values step, a step, its output, a comparison, an amount, and an optional second step and its output, and is checked the same way, a comparison with no second step checking the first answer against the amount alone, following its `true` connection when every comparison holds for `all of` or at least one holds for `any of`, and its `false` connection otherwise, including when it holds no comparisons, a comparison whose answer is missing, is an array or is not a number counting as not holding) — Count (`:count`, which acts without the visitor and records a whole number, how many visits to an earlier step the admin picks the run has recorded, counting only visits that gave the answer the admin picks or every visit when the admin picks none, reading that answer from the output the admin picks when the earlier step recorded several, named on the canvas `Count` followed by the earlier step's id and the answer, such as `Count tests failed`, and recording a new count under its own visit key each time a loop reaches it, so a compare step after it can end a loop once the count reaches an amount), Wait (`:wait`, which holds the run until a time worked out from an earlier step the admin picks as `Counting from`; see "Hold a run until a time"), and Flow (`:flow_step`, names another flow of the same host and the number of one of its versions, with one connection for each different output that version's End steps write, and named on the canvas by that flow's title and the version number, such as `Intake, version 2`). When a stored run stops at a Flow step, a run of the named flow is started on the named version, and the first run waits on the Flow step. When the second run ends with an output, that output is recorded in the first run at the Flow step, and the first run moves on along the connection labelled with that output. When the second run ends with no output, nothing is recorded and the first run stays waiting at the Flow step. On a question and on a checklist the admin can give each answer info, a longer explanation the visitor opens with an info button beside that answer. An admin may connect a step back to an earlier one, so a flow can ask the same question more than once, and each visit's answer is kept. The time each step visit was completed is kept together with its recorded value; see "Read when steps were completed". A File upload step takes a file only in a flow the admin set to save each step. In a flow set to save on finish or to save nothing, a visitor who chooses a file is shown `A file can only be uploaded in a flow that saves its runs.` and cannot go on with it. Before writing a step type to ask for one answer or several, to take a file, to branch on an answer or a number, to branch on how two earlier answers compare, to branch on whether all or any of several comparisons hold, to branch on when an earlier step was completed, to count the visits to an earlier step, or to hold a run until a time, ask the developer whether an admin placing one of the built-in types on the canvas is enough. None of condition, switch, compare, Compare two values or All of or Any of reads a checklist's answer, so branching on what a visitor ticked takes a step type of the app's own that defines `route`. Use this local when the app needs a step type of its own, needs a host's flows on its own pages with its own behaviour when a visitor finishes, needs a visitor's earlier answers to decide which answers a later step offers, or needs to read what a visitor answered. It assumes easy_flow is already installed and a host is declared; if not, hand off to `easy_flow-install` first.
|
|
12
|
+
A Rails engine for flows an admin draws on a canvas and a visitor runs one step at a time. Each step is an instance of a step type. The engine ships thirteen: start (`:start`), end (`:terminal`, whose one setting is the output an admin writes for a flow that ends there), question (`:question`, a question with a list of answers of which the visitor picks one, which the admin can mark required so a blank answer is refused), checklist (`:checklist`, a question with a list of answers of which the visitor ticks any number, which the admin can mark required so a visitor who ticks nothing is refused), File upload (`:file_upload`, a question the visitor answers by choosing one file from a file upload control labelled with the question text, with an admin setting listing the accepted file types as comma-separated extensions such as `.pdf, .png`, which limits the visitor's file picker to those types and is shown under the control as `Accepts` followed by the list as the admin wrote it, such as `Accepts .pdf, .png`, which the admin can mark required so a visitor who chooses no file is refused, and whose recorded answer is the reference the host's file store returned for the file), five that pick a branch from earlier answers with no code — condition (`:condition`, is or is not a chosen value, where the admin must pick one of the earlier step's outputs and the answer is read from that output when the earlier step recorded several and whole otherwise, so a Condition cannot be completed on a step whose type declares no output), switch (`:switch`, follows the connection labelled with the answer), compare (`:compare`, reads the answer as a number, from the output the admin picks when the earlier step recorded several, and checks it by more than, less than, at least or at most against an amount) and Compare two values (`:compare_two_values`, reads the answers of two earlier steps the admin picks as numbers, reading each from the output the admin picks when that step recorded several, and checks the first by more than, less than, at least, at most or equal to against the second plus an amount the admin may give, a blank amount adding nothing, checking the first against the amount alone when the admin leaves the second step empty, following its `true` or `false` connection, and following `false` when either answer is missing, is an array or is not a number) and All of or Any of (`:all_or_any`, holds a list of comparisons the admin enters and a `Must hold` setting of `all of` or `any of`, where each comparison asks for the same things as a Compare two values step, a step, its output, a comparison, an amount, and an optional second step and its output, and is checked the same way, a comparison with no second step checking the first answer against the amount alone, following its `true` connection when every comparison holds for `all of` or at least one holds for `any of`, and its `false` connection otherwise, including when it holds no comparisons, a comparison whose answer is missing, is an array or is not a number counting as not holding) — Count (`:count`, which acts without the visitor and records a whole number, how many visits to an earlier step the admin picks the run has recorded, counting only visits that gave the answer the admin picks or every visit when the admin picks none, reading that answer from the output the admin picks when the earlier step recorded several, named on the canvas `Count` followed by the earlier step's id and the answer, such as `Count tests failed`, and recording a new count under its own visit key each time a loop reaches it, so a compare step after it can end a loop once the count reaches an amount), Wait (`:wait`, which holds the run until a time worked out from an earlier step the admin picks as `Counting from`; see "Hold a run until a time"), and Flow (`:flow_step`, names another flow of the same host and the number of one of its versions, with one connection for each different output that version's End steps write, and named on the canvas by that flow's title and the version number, such as `Intake, version 2`). On a compare, Compare two values or Count step, and in each comparison of an All of or Any of step, once the admin picks an earlier step, the admin must also pick which of that step's outputs to read: until then the step's card shows `missing setting: output`, `missing setting: other_output` for a Compare two values step's second step, or `missing setting: comparisons <n> output` and `missing setting: comparisons <n> other_output` for the comparison at position `n` counting from 1, and saving the flow as a new version and publishing it are refused. When a stored run stops at a Flow step, a run of the named flow is started on the named version, and the first run waits on the Flow step. When the second run ends with an output, that output is recorded in the first run at the Flow step, and the first run moves on along the connection labelled with that output. When the second run ends with no output, nothing is recorded and the first run stays waiting at the Flow step. On a question and on a checklist the admin can give each answer info, a longer explanation the visitor opens with an info button beside that answer. An admin may connect a step back to an earlier one, so a flow can ask the same question more than once, and each visit's answer is kept. The time each step visit was completed is kept together with its recorded value; see "Read when steps were completed". A File upload step takes a file only in a flow the admin set to save each step. In a flow set to save on finish or to save nothing, a visitor who chooses a file is shown `A file can only be uploaded in a flow that saves its runs.` and cannot go on with it. Before writing a step type to ask for one answer or several, to take a file, to branch on an answer or a number, to branch on how two earlier answers compare, to branch on whether all or any of several comparisons hold, to branch on when an earlier step was completed, to count the visits to an earlier step, or to hold a run until a time, ask the developer whether an admin placing one of the built-in types on the canvas is enough. None of condition, switch, compare, Compare two values or All of or Any of reads a checklist's answer, so branching on what a visitor ticked takes a step type of the app's own that defines `route`. Use this local when the app needs a step type of its own, needs a host's flows on its own pages with its own behaviour when a visitor finishes, needs a visitor's earlier answers to decide which answers a later step offers, or needs to read what a visitor answered. It assumes easy_flow is already installed and a host is declared; if not, hand off to `easy_flow-install` first.
|
|
13
13
|
|
|
14
14
|
## Interface
|
|
15
15
|
|
|
@@ -79,7 +79,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
79
79
|
4. Ask the developer which hosts' admins should be able to add the type. A registered type is in a host's palette, the step types an admin can add on that host's canvas, only when the host names no list of offered step types or its list includes the type's id. A type that declares `ends_here` is in every host's palette, and a type that declares `begins_here` is in none. When a host that names a list should offer the new type, add the type's id to that host's list through `easy_flow-install`. Leaving a type off a host's list only keeps admins from adding it there, and a step of that type already in one of the host's flows is still drawn and run.
|
|
80
80
|
5. Use these words to declare the type. Each is called once at class level (or inside the `EasyFlow.step` block):
|
|
81
81
|
- `step_name "<label>"` — the name admins see on the canvas. Defaults to the id.
|
|
82
|
-
- `setting :name, type:, label:, options:, required:, limit:, check:, from:, outputs_of:, kept_on:, attribute:` — a field the admin fills in on the canvas. `type` is one of `:string`, `:integer`, `:float`, `:boolean`, `:select`, `:multi_select`, `:previous_step`, `:from_step`, `:list`, `:flow`, or boot raises `EasyFlow::UnknownFieldType`. A `:select` or `:multi_select` must pass `options:`, or boot raises the same error. `options:` is either an array of strings, or a lambda taking no arguments that returns one; the lambda is called each time the canvas is opened and each time an admin saves the step's settings, so the options follow the app's data without a restart (`options: -> { Region.order(:name).pluck(:code) }`). A saved setting whose value is no longer among the options is refused the next time the admin saves that step. When the options come from the app's records, ask the developer which records, which column is stored as the value, and whether the list must differ by account or host, since the lambda is passed nothing and the same list is offered everywhere the type is used. A `:list` must take a block of `setting` calls describing one entry. `:previous_step` lets the admin pick an earlier step, and is
|
|
82
|
+
- `setting :name, type:, label:, options:, required:, optional:, limit:, check:, from:, outputs_of:, kept_on:, attribute:` — a field the admin fills in on the canvas. `type` is one of `:string`, `:integer`, `:float`, `:boolean`, `:select`, `:multi_select`, `:previous_step`, `:from_step`, `:list`, `:flow`, or boot raises `EasyFlow::UnknownFieldType`. A `:select` or `:multi_select` must pass `options:`, or boot raises the same error. `options:` is either an array of strings, or a lambda taking no arguments that returns one; the lambda is called each time the canvas is opened and each time an admin saves the step's settings, so the options follow the app's data without a restart (`options: -> { Region.order(:name).pluck(:code) }`). A saved setting whose value is no longer among the options is refused the next time the admin saves that step. When the options come from the app's records, ask the developer which records, which column is stored as the value, and whether the list must differ by account or host, since the lambda is passed nothing and the same list is offered everywhere the type is used. A `:list` must take a block of `setting` calls describing one entry. `:previous_step` lets the admin pick an earlier step, and is required unless declared with `optional: true`. Ask the developer whether a step of the type may be left without that earlier step, and when it may, declare `optional: true` and handle a blank value in the type's code. `outputs_of: :<setting>` offers the outputs of the step chosen in that setting, and `from: :<setting>` offers the values of the output chosen in that setting; either one makes the type `:from_step`. A required setting left blank shows `missing setting: <name>` on the step's card, and saving the flow as a new version and publishing it are refused until the admin fills it in. An `outputs_of:` setting left blank is flagged and refused the same way once the setting it reads names a step, whether or not it is declared `required:`. Inside a `:list` entry, a blank required setting, or a blank `outputs_of:` setting whose step is picked, shows `missing setting: <list> <n> <name>` for the entry at position `n` counting from 1. `optional:` has no effect inside a `:list` entry, where a `:previous_step` is required only when declared `required: true`. `:flow` lets the admin pick one of the flows the admin's list shows on the same host, offered by title, and is stored in `node.config` as that flow's id as a string. A `:flow` setting is not checked on save against the flows offered, so code that reads it looks the flow up through the host and handles a blank or unknown id. `limit:` is the most values a `:multi_select` takes. `check:` is a lambda given the value that returns an error message, or `nil` when the value is acceptable. `label:` defaults to the name humanized. `kept_on:` and `attribute:` keep the setting's value on one of the app's own records instead of in the flow; see "Keep a setting on one of the app's records".
|
|
83
83
|
- `output :name, type:, label:, values:, from:` — a value the step records. `type` is one of `:string`, `:integer`, `:float`, `:boolean`, or boot raises `EasyFlow::UnknownOutputType`. `values:` is an array or a lambda taking the node, listing the values the output can take; the canvas offers these as the connections leaving the step, so a type that routes must declare them. `from: :<setting>` takes the values from the step chosen in that setting. A Condition step requires the admin to pick one of the earlier step's outputs, so a type whose answer admins should test with a Condition declares at least one `output`, with `values:` so the Condition offers answers to pick.
|
|
84
84
|
- `outputs_named_by :<list setting>` — adds one output for each entry an admin enters in that `:list` setting, named by the entry's `name`, so the setting's entry block must declare `setting :name, type: :string`. Ask the developer whether the admin or the type decides the names of the values the step records. Do not pick.
|
|
85
85
|
- The named outputs follow any outputs the type declares with `output`, in the order the admin entered them. An entry with a blank `name` adds none.
|
|
@@ -155,6 +155,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
155
155
|
- The flow is found by its id alone, not through the host or owner. Ask the developer which flows the block may name, and look the flow up through the run's flow's host or the run's owner inside the block when it must be limited.
|
|
156
156
|
- When the block returns `nil`, a blank `flow:`, or the id of no existing flow, `EasyFlow::InnerFlowError` is raised with a message naming the step, such as `The step pick chose no flow to run`, and no run is started. A retired or withdrawn version raises `EasyFlow::InnerFlowError` as for a Flow step, and a blank version number, one naming no version of the flow, or one naming a draft raises `ActiveRecord::RecordInvalid`.
|
|
157
157
|
- When the started run ends with an output, that output is recorded under the step's id as for a Flow step. When it ends with no output, `"ended"` is recorded instead, and the parent run moves on. A type that routes on what is recorded declares an `output` whose `values:` include `"ended"` beside the outputs the chosen flows can end with. Ask the developer what `"ended"` should lead to.
|
|
158
|
+
- When a loop stops the started run, nothing is recorded, not even `"ended"`, no job is queued, and the parent run stays waiting at the step. See step 5 of "Follow a run into the flow a Flow step started".
|
|
158
159
|
- A flow set to save on finish or to save nothing, and an admin's preview, keep no stored run, so the block is not called and no run is started.
|
|
159
160
|
|
|
160
161
|
```ruby
|
|
@@ -376,24 +377,29 @@ Use this when a visitor's run reaches a Flow step and the app must take the visi
|
|
|
376
377
|
- When the parent run then ends and was itself started by a Flow step, its own output is handed to its parent the same way.
|
|
377
378
|
- The new run's own `finished` is still called on its visitor's page load. The job does not call `finished` for the parent run, and if the parent run reaches its end, `finished` is called the next time its visitor loads its page.
|
|
378
379
|
- A visitor who presses Back on the step after a Flow step removes the recorded output, and the parent run waits on the Flow step again. No second run is started, and the parent run moves on again only when the new run's page is loaded or `advance` is called on it.
|
|
379
|
-
4. What holds when the new run ends with no output, which happens when it ends on an End step whose output was left blank, or ends without reaching an End step, and the step that started it is not of a type that declares `chooses_flow_from_run` (for that type, `"ended"` is recorded and the parent run moves on):
|
|
380
|
+
4. What holds when the new run ends with no output, which happens when it ends on an End step whose output was left blank, or ends without reaching an End step for any reason other than a loop stopping it, and the step that started it is not of a type that declares `chooses_flow_from_run` (for that type, `"ended"` is recorded and the parent run moves on):
|
|
380
381
|
- Nothing is recorded in the parent run, and the parent run stays at the Flow step showing `Waiting for <name>.` on every page load. Nothing in the engine moves it on.
|
|
381
382
|
- A job is still queued that fails with `EasyFlow::InnerFlowError` and a message naming the Flow step's id, such as `Nothing was recorded at the Flow step sub, so its run cannot move on`. The job does not move the parent run.
|
|
382
383
|
- Each later page load of the new run, and each later `advance` on it, queues another job that fails the same way.
|
|
383
384
|
- What happens to a failed job, such as a retry or a report, is decided by the app's Active Job adapter and error reporting.
|
|
384
385
|
- The new run's own `finished` is still called on its visitor's page load.
|
|
385
|
-
5. What holds when
|
|
386
|
+
5. What holds when a loop stops the new run, which is when `inner.stopped_on_loop` names a step (see "Read a run and its answers"), whatever type the step that started it is:
|
|
387
|
+
- Nothing is recorded in the parent run, not even `"ended"` for a step of a type that declares `chooses_flow_from_run`.
|
|
388
|
+
- No job is queued, on the page load that stops the new run or on any later page load or `advance` of it, so no `EasyFlow::InnerFlowError` is raised for it.
|
|
389
|
+
- The parent run stays at the step showing `Waiting for <name>.` on every page load, and `parent.waiting_on` is still the stopped run. Nothing in the engine moves it on.
|
|
390
|
+
- The new run's own `finished` is still called on its visitor's page load.
|
|
391
|
+
6. What holds when an admin removes a flow:
|
|
386
392
|
- Every run of the removed flow is deleted with it.
|
|
387
393
|
- A run that one of those runs' Flow steps started is kept. Its `parent_run` becomes `nil` and its `parent_step` keeps the step id.
|
|
388
394
|
- When such a kept run ends, nothing is recorded anywhere and no job is queued.
|
|
389
395
|
- Removing a flow is refused while one of its runs is the run that a run of another flow is stopped at a Flow step waiting on, so a waiting run's `waiting_on` is never deleted this way.
|
|
390
396
|
- A run of the removed flow that was started by a Flow step but is no longer waited on is deleted, so `run.inner_runs.find_by(parent_step: step_id)` returns `nil` for that step afterwards.
|
|
391
|
-
|
|
397
|
+
7. Find the runs:
|
|
392
398
|
- `run.waiting_on` — the run started at the step `run` is stopped at, or `nil` when `run` is stopped at a step that started no run, or has reached its end.
|
|
393
399
|
- `run.inner_runs.find_by(parent_step: step_id)` — the run started at a given step, or visit key such as `"sub@2"`, whether or not `run` is still stopped there.
|
|
394
400
|
- `run.parent_run` — the run that started `run`.
|
|
395
|
-
|
|
396
|
-
|
|
401
|
+
8. Nothing takes the visitor from the parent run to the new run. Ask the developer how the visitor gets there, such as a link on the app's own page or a redirect from the app's controller, and where to show the new run, which is the `_run` route of the controller serving its host, such as `main_app.intake_run_path(run.waiting_on)`. Do not pick.
|
|
402
|
+
9. Nothing takes the visitor back to the parent run when the new run ends either. Ask the developer whether the visitor goes back, and if so override `finished(answers, run)` in the controller serving the new run's host to redirect when `run&.parent_run` is present, to the `_run` route of the controller serving the parent run's host. Check `run&.parent_run` and not `parent_step`, since a run whose parent flow was removed keeps its `parent_step` with no `parent_run`:
|
|
397
403
|
|
|
398
404
|
```ruby
|
|
399
405
|
def finished(answers, run)
|
|
@@ -403,11 +409,12 @@ Use this when a visitor's run reaches a Flow step and the app must take the visi
|
|
|
403
409
|
end
|
|
404
410
|
```
|
|
405
411
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
412
|
+
10. The new run already belongs to the parent run's owner, so a run that should have an owner needs it set on the parent run before the parent reaches the Flow step, such as in `start_run(flow)`. Ask the developer whether the new run should belong to a different record, and if so set it where the app sends the visitor on, for example `run.waiting_on.update!(owner: ...)`.
|
|
413
|
+
11. Ask the developer what the visitor is shown when the parent run cannot start its new run, since `ActiveRecord::RecordNotFound`, `EasyFlow::InnerFlowError` or `ActiveRecord::RecordInvalid` is raised on the visitor's page load.
|
|
414
|
+
12. Ask the developer what each output of the named flow should lead to in the parent flow. Each different output on the named version's End steps is a connection leaving the Flow step on the canvas, which an admin connects, so no code is needed unless the app acts on the output itself. Only outputs written on built-in End steps are offered as connections, so a named flow that ends on a step of the app's own `ends_here` type hands over an output that matches no connection, and the parent run ends at the Flow step.
|
|
415
|
+
13. Ask the developer what should happen when a named flow ends with no output and its parent run is left waiting, such as admins writing an output on every End step of a flow another flow names, or the app watching for the failed job's `EasyFlow::InnerFlowError` and acting on the parent run. Do not pick. Publishing does not refuse a named flow whose End step has a blank output.
|
|
416
|
+
14. Ask the developer what should happen when a loop stops a named flow's run and its parent run is left waiting. Do not pick. No job fails for it, so the app finds such a parent by its `waiting_on` run having a `stopped_on_loop`, such as `parent.waiting_on&.stopped_on_loop`.
|
|
417
|
+
15. Publish a flow with a Flow step set to save each step, start a run with an owner, walk it to the Flow step, and check `run.waiting_on` is one run of the named flow with that run as its `parent_run` and the same `owner`, and that reloading the page starts no second one. Walk the new run to an End step, and check the parent run records that End step's output at the Flow step and moves on along the connection labelled with it. Walk another new run to an End step with a blank output, and check the parent run stays waiting and the queued job fails with `EasyFlow::InnerFlowError`. Walk another new run of a flow with a loop of Count steps only until the loop stops it, and check the parent run records nothing, stays waiting, and no job is queued. While a parent run waits on a new run, check that removing the named flow is refused. Remove the parent run's flow, and check the new run is kept with no `parent_run` and that finishing it neither raises nor queues a job.
|
|
411
418
|
|
|
412
419
|
### Decide which answers a step offers in a run
|
|
413
420
|
|
|
@@ -476,7 +483,7 @@ Use this when a visitor's run should stop until a set time has passed, such as a
|
|
|
476
483
|
- `Next time of day after it, as HH:MM` — the run is held until the first time the clock reads that hour and minute after the counted time, in the app's time zone, which is the same day when that is still to come and the next day otherwise.
|
|
477
484
|
- `Date, as YYYY-MM-DD` — the run is held until the start of that date in the app's time zone.
|
|
478
485
|
- A date wins over a time of day, and a time of day wins over minutes. The time of day and the date are not checked on save, so a value in another form is not refused.
|
|
479
|
-
- The step is named on the canvas and in the waiting message by what it waits for: the date as the admin wrote it, such as `2026-10-12`, or else the time of day followed by `after` and the counted step's id, such as `09:00 after ask`, or else the minutes followed by `minutes after` and the counted step's id, such as `30 minutes after ask`. When the date, the time of day and the minutes are all blank, the step is named by its id on the canvas and the waiting message reads `Waiting for Wait.`, and the run moves on as soon as the counted step has a time.
|
|
486
|
+
- The step is named on the canvas and in the waiting message by what it waits for: the date as the admin wrote it, such as `2026-10-12`, or else the time of day followed by `after` and the counted step's id, such as `09:00 after ask`, or else the minutes followed by `minutes after` and the counted step's id, such as `30 minutes after ask`, or `minute after` when the minutes are 1, such as `1 minute after ask`. When the date, the time of day and the minutes are all blank, the step is named by its id on the canvas and the waiting message reads `Waiting for Wait.`, and the run moves on as soon as the counted step has a time.
|
|
480
487
|
3. What then holds:
|
|
481
488
|
- Without a date, the run is held for as long as the counted step has no time, such as a step that only routes or a step not on the path taken.
|
|
482
489
|
- While held, the visitor is shown `Waiting for <name>.`, such as `Waiting for 30 minutes after ask.`, with no form, no Next button and no Back button.
|
|
@@ -494,15 +501,15 @@ Use this when a visitor's run should stop until a set time has passed, such as a
|
|
|
494
501
|
- `run.held_until` — the time the run is held until while stopped at a Wait step, or `nil`. See "Hold a run until a time".
|
|
495
502
|
- `run.flow` — the flow it belongs to.
|
|
496
503
|
- `run.owner` — the optional record the run belongs to, polymorphic, set by the app. `run.label` and `run.status` are free string columns for the app's own use.
|
|
497
|
-
- `run.parent_run` and `run.parent_step` — the run whose Flow step started this one and that step's id, both `nil` for a run a visitor or the app started. Once this run ends with an output, `run.parent_run.recorded` holds that `output` under that step id. When it ends with none, nothing is recorded there. When an admin removed the flow the parent run belonged to, `parent_run` is `nil` and `parent_step` still holds the step id.
|
|
504
|
+
- `run.parent_run` and `run.parent_step` — the run whose Flow step started this one and that step's id, both `nil` for a run a visitor or the app started. Once this run ends with an output, `run.parent_run.recorded` holds that `output` under that step id. When it ends with none, nothing is recorded there. When a loop stops it, nothing is recorded there and no job is queued. When an admin removed the flow the parent run belonged to, `parent_run` is `nil` and `parent_step` still holds the step id.
|
|
498
505
|
- `run.inner_runs` — the runs this run's Flow steps started.
|
|
499
506
|
- `run.waiting_on` — the run started at the step this run is stopped at, or `nil` when no run was started there.
|
|
500
507
|
- `run.pinned_definition` — the flow document of the version the run started on.
|
|
501
508
|
- `run.next_step(answers)` and `run.walked(answers)` — the next step, and the answers on the path taken, for a set of answers against that pinned version, routed with the run's own `completed_at`.
|
|
502
509
|
- `run.output` — the text the admin wrote into the output setting of the End step the run ended on, read from the run's pinned version. It is `nil` when the run has not reached an End step, or ended on one whose output was left blank. A flow may have several End steps with a different output on each, so the output says which way the flow ended. Ask the developer what the app does with each output, and what it does when there is none.
|
|
503
510
|
- `run.ended_at_end?` — `true` when the run is not stopped at a step that awaits input, computes a value or waits, and the last step it reached is of a type that declares `ends_here`, such as the built-in End step, whether or not that step has an output. It is `false` while the run is still going, including while it waits at a Flow step, when a loop stopped it, and when it ended at a step that is not an end, such as a step with no connection leaving it or a `route` value that matches no connection's label. Use it to tell a run that ended at an End step with a blank output from one that stopped anywhere else, since `run.output` is `nil` for both.
|
|
504
|
-
- `run.stopped_on_loop` — the id of the step a loop came back to when it stopped the run, as a string, the plain step id and never a visit key such as `"tally@2"`, or `nil` when no loop stopped it. A loop stops the run when it comes back to a step already visited with no step that awaits input answered since that step's last visit, such as a loop of Count steps only. The run ends there, and `run.ended_at_end?` is `false`. Ask the developer what the app does with a run a loop stopped, such as showing an admin the step named. Do not pick.
|
|
505
|
-
- `run.advance` — moves the run past every waiting step whose `waits_until` block now returns a truthy value, and past every step that computes a value, and records each result on the run. It stops at the next step that asks the visitor for input, the next waiting step that is not ready, or the end. It does not call `finished`; that is called the next time the visitor loads the run's page. When it stops at a Flow step it starts that step's run, and when it reaches the end of a run a Flow step started it hands the output to the parent run; see "Follow a run into the flow a Flow step started".
|
|
511
|
+
- `run.stopped_on_loop` — the id of the step a loop came back to when it stopped the run, as a string, the plain step id and never a visit key such as `"tally@2"`, or `nil` when no loop stopped it. A loop stops the run when it comes back to a step already visited with no step that awaits input answered since that step's last visit, such as a loop of Count steps only. The run ends there, and `run.ended_at_end?` is `false`. When a Flow step started the run, nothing is handed to its parent run, which stays waiting at that step; see step 5 of "Follow a run into the flow a Flow step started". Ask the developer what the app does with a run a loop stopped, such as showing an admin the step named. Do not pick.
|
|
512
|
+
- `run.advance` — moves the run past every waiting step whose `waits_until` block now returns a truthy value, and past every step that computes a value, and records each result on the run. It stops at the next step that asks the visitor for input, the next waiting step that is not ready, or the end. It does not call `finished`; that is called the next time the visitor loads the run's page. When it stops at a Flow step it starts that step's run, and when it reaches the end of a run a Flow step started it hands the output to the parent run, unless a loop stopped the run; see "Follow a run into the flow a Flow step started".
|
|
506
513
|
3. To show answers with their question text and answer labels, build a runner from the run's pinned document:
|
|
507
514
|
|
|
508
515
|
```ruby
|
|
@@ -536,12 +543,13 @@ Use this when a visitor's run should stop until a set time has passed, such as a
|
|
|
536
543
|
- A `chooses_flow` block is given only the step, finds the flow by id with no host or owner scope, and is called on every canvas draw, name shown, save and publish, so it limits the flows it names itself and changes nothing.
|
|
537
544
|
- A step of a `chooses_flow` type with no `names_by` is named by the chosen flow's title and version number.
|
|
538
545
|
- A `chooses_flow_from_run` block is called only when a stored run stops at the step, finds the flow by id with no host or owner scope, and is never checked on save or publish, so the block limits the flows it names itself.
|
|
539
|
-
- A run started by a `chooses_flow_from_run` step that ends with no output records `"ended"` at that step, and the parent run moves on.
|
|
546
|
+
- A run started by a `chooses_flow_from_run` step that ends with no output records `"ended"` at that step, and the parent run moves on, unless a loop stopped it.
|
|
540
547
|
- A host's `allowed_answers` lambda is given only the step and the answers recorded so far, and is called many times per page load, so it reads only those and changes nothing.
|
|
541
548
|
- A step the host allows one answer is recorded without being shown, and one it allows none is recorded as `""` and skipped, whatever the step's type.
|
|
542
549
|
- A runner the app builds without `host:` ignores the host's `allowed_answers`.
|
|
543
550
|
- A flow holding a `starts_a_flow` step whose `flow` names no existing flow, or whose `version` names no live or replaced version of it, or whose `flow` leads back to the flow the step sits in directly or through other flows, cannot be saved as a version or published, and these checks cannot be turned off.
|
|
544
551
|
- On the canvas, clicking a step of a `starts_a_flow` type whose `flow` setting names a flow goes to that flow's page, which has a `Back to` button to the flow the step is in, and the step's settings open from its `Settings` button.
|
|
552
|
+
- A `:previous_step` setting is required unless declared `optional: true`, and an `outputs_of:` setting must be filled once the step it reads is picked, or the flow cannot be saved as a version or published.
|
|
545
553
|
- A type that routes declares the values its output takes, or the canvas offers no connections to label.
|
|
546
554
|
- An output named by `outputs_named_by` takes its name from an entry's `name`, lists no values, and is never a connection leaving the step.
|
|
547
555
|
- A run starts only on a version its own flow has published and not retired or withdrawn, and any other version raises `ActiveRecord::RecordInvalid`.
|
|
@@ -569,6 +577,7 @@ Use this when a visitor's run should stop until a set time has passed, such as a
|
|
|
569
577
|
- A run started by a Flow step takes the owner its parent run had when it was started, and nothing sends the visitor to it or back from it.
|
|
570
578
|
- A run that reaches a Flow step waits there until the run it started ends with an output, then records that output and follows the connection labelled with it.
|
|
571
579
|
- A run started by a Flow step that ends with no output leaves its parent run waiting at the Flow step, and each job queued to move the parent on fails with `EasyFlow::InnerFlowError`.
|
|
580
|
+
- A run started by a Flow step or any `starts_a_flow` step that a loop stopped hands nothing to its parent run and queues no job, so the parent run waits at that step with nothing failing to report it.
|
|
572
581
|
- A Flow step whose named version was retired or withdrawn after publishing raises `EasyFlow::InnerFlowError` naming the step when a run stops there.
|
|
573
582
|
- A parent run is moved on by an Active Job job, so the app's queue adapter must run jobs for it to move on without its visitor reloading.
|
|
574
583
|
- Only a stored run starts a run at a Flow step.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: easy_flow-info
|
|
3
|
-
description: Use to learn what easy_flow offers — guided flows an admin draws on a canvas and a visitor runs one step at a time, versioned documents of steps and connections, flows that loop back to ask a step again, a loop that stops a run when nothing new was answered and the step it came back to, whether a run ended at an End step, hosts and the step types each offers its admins, runs and the published version each starts on, each visit a run records with its value and the time the step was completed, and the output a flow gives when it ends, the built-in step types including questions and checklists that can require an answer and explain each answer behind an info button, file upload steps that hand a visitor's file to the app's file store and read it back by its name, and branching on an answer, including one named output of a step that recorded several, on comparing a number with an amount, or on comparing two earlier answers, branching on a list of such comparisons that must all hold or of which any one must hold, branching on the hour, weekday or minute a step was completed, Wait steps that hold a run until a number of minutes or a time of day after an earlier step was completed, or until a date, and are named after what they wait for, the Check again button a visitor sees at a waiting step in a flow that keeps nothing, Count steps that count how often an earlier step was answered so a loop can give up after a number of tries, Flow steps that name another flow, open its canvas when their card is clicked with a button back to the flow it was opened from, start a run of it for the same owner when a stored run reaches them, are flagged when the flow or version they name cannot run or leads back to the flow they sit in, let the waiting run name that inner run, move that run on along the output it ends with, and fail with an error naming the step when its version was taken out of service or its inner run ended with no output, removing a flow and why it is refused while a waiting run depends on it, step types the host app registers, including ones whose outputs are named by the entries an admin lists in one of their settings, ones that start another flow and work out which flow and version to run from their own settings and are named after that flow, or choose it from the run they are in, the answers a host allows a step in a run and the steps it fills in or passes over, the checks they make on an answer, what a step shows a visitor and how that can depend on the visitor's run, and steps that hold a run until something outside the flow has happened, and the settings an admin fills in, including picks whose options come from the app's data and settings kept on one of the app's own records.
|
|
3
|
+
description: Use to learn what easy_flow offers — guided flows an admin draws on a canvas and a visitor runs one step at a time, versioned documents of steps and connections, flows that loop back to ask a step again, a loop that stops a run when nothing new was answered and the step it came back to, whether a run ended at an End step, hosts and the step types each offers its admins, runs and the published version each starts on, each visit a run records with its value and the time the step was completed, and the output a flow gives when it ends, the built-in step types including questions and checklists that can require an answer and explain each answer behind an info button, file upload steps that hand a visitor's file to the app's file store and read it back by its name, and branching on an answer, including one named output of a step that recorded several, on comparing a number with an amount, or on comparing two earlier answers or one earlier answer with an amount alone, steps flagged when they name an earlier step without choosing which of its outputs to read, branching on a list of such comparisons that must all hold or of which any one must hold, branching on the hour, weekday or minute a step was completed, Wait steps that hold a run until a number of minutes or a time of day after an earlier step was completed, or until a date, and are named after what they wait for, the Check again button a visitor sees at a waiting step in a flow that keeps nothing, Count steps that count how often an earlier step was answered so a loop can give up after a number of tries, Flow steps that name another flow, open its canvas when their card is clicked with a button back to the flow it was opened from, start a run of it for the same owner when a stored run reaches them, are flagged when the flow or version they name cannot run or leads back to the flow they sit in, let the waiting run name that inner run, move that run on along the output it ends with, leave it waiting when the inner run is stopped by a loop, and fail with an error naming the step when its version was taken out of service or its inner run ended with no output, removing a flow and why it is refused while a waiting run depends on it, step types the host app registers, including ones whose outputs are named by the entries an admin lists in one of their settings, ones that start another flow and work out which flow and version to run from their own settings and are named after that flow, or choose it from the run they are in, the answers a host allows a step in a run and the steps it fills in or passes over, the checks they make on an answer, what a step shows a visitor and how that can depend on the visitor's run, and steps that hold a run until something outside the flow has happened, and the settings an admin fills in, including picks whose options come from the app's data and settings kept on one of the app's own records.
|
|
4
4
|
tools: Read
|
|
5
5
|
scope: guided flows — versioned documents of steps and the connections between them, drawn on a canvas by an admin and run by a visitor one step at a time, with step types the host registers
|
|
6
6
|
---
|
|
@@ -55,6 +55,7 @@ easy_flow declares no commands of its own for this local. Its surface is split b
|
|
|
55
55
|
- **Options** — the values a pick-from-a-list setting offers. They are either a fixed list written into the step type, or a lookup the app provides that is read each time the canvas or a save asks for them, so they can come from the app's own data. A saved value that is not among the options is refused.
|
|
56
56
|
- **Kept setting** — a setting whose value lives on one of the app's own records rather than in the flow. The step type says how to find that record from the step's other settings and which of its attributes holds the value. The canvas shows the value read from the record, and saving the step's settings writes it straight to the record, so the flow's document and its versions never hold it and undo or publishing does not change it. When no record is found, nothing is written. When the record refuses a value, the save is refused with the record's own error and none of the step's kept settings are written.
|
|
57
57
|
- **Output** — a value a step records into the run, with a type and a label. Later steps read outputs, and a setting that points at an earlier step offers that step's known values on the canvas. A step type may also take further outputs from a list setting, one for each entry the admin gives a name, labelled with that name written as a phrase, such as "First reading" for `first_reading`. An entry left without a name adds no output. Those outputs are offered wherever the step's declared outputs are, such as when a Condition, Compare, Compare two values, All of or Any of or Count step picks which output to read.
|
|
58
|
+
- **Chosen output** — which of an earlier step's outputs a step reads. Every built-in step that names an earlier step and then reads one of its outputs asks the admin to pick that output, as does a step type of the app's own whose output setting points at a step setting. Once the earlier step is named, a step with no output picked is reported as missing that setting, and the same holds for each comparison in an All of or Any of list, for its first step and for its second step. A step that names no earlier step, such as a Compare two values step with its second step left empty, is not asked for that step's output.
|
|
58
59
|
- **Start** and **End** — the built-in step types that begin and finish a flow. An End step has one setting, its output.
|
|
59
60
|
- **Flow output** — the text an admin writes into the output setting of an End step, given as the result of a stored run that has reached that step. A flow may hold several End steps with a different output on each, so the output says which way the flow ended. A run that has not reached an End step, or that ended on one whose output was left blank, gives no output.
|
|
60
61
|
- **Question** — the built-in step type that asks the visitor: a question text, an optional category, a yes-or-no required setting, and a list of answers, each with a value, a label, a weight, a hint and an info. The visitor picks one answer, and that answer's value is recorded.
|
|
@@ -68,21 +69,21 @@ easy_flow declares no commands of its own for this local. Its surface is split b
|
|
|
68
69
|
- **Required checklist** — a checklist step the admin has marked required. Submitting it with nothing ticked is refused with the message "Tick at least one to go on."
|
|
69
70
|
- **Condition** — a built-in branching step that checks whether an earlier step's answer is, or is not, a chosen value, and follows the true or the false connection. The admin must also pick which of that step's outputs to test, and a Condition step with none picked is reported as missing that setting. When the earlier step recorded several outputs, only the picked one is tested, and when it recorded a single answer, that answer is tested whole.
|
|
70
71
|
- **Switch** — a built-in branching step that follows the connection labelled with an earlier step's answer.
|
|
71
|
-
- **Compare** — a built-in branching step that reads an earlier step's answer as a number and checks it against an amount by more than, less than, at least or at most, then follows the true or the false connection.
|
|
72
|
-
- **Compare two values** — a built-in branching step that reads two earlier steps' answers as numbers and checks the first against the second by more than, less than, at least, at most or equal to, then follows the true or the false connection. For each side
|
|
73
|
-
- **All of or Any of** — a built-in branching step that holds a list of comparisons and a "Must hold" setting of "all of" or "any of", then follows the true or the false connection. Each comparison names an earlier step and
|
|
72
|
+
- **Compare** — a built-in branching step that reads an earlier step's answer as a number and checks it against an amount by more than, less than, at least or at most, then follows the true or the false connection. The admin picks which of the earlier step's outputs to read, as described under chosen output. An answer that is missing or is not a number decides false.
|
|
73
|
+
- **Compare two values** — a built-in branching step that reads two earlier steps' answers as numbers and checks the first against the second by more than, less than, at least, at most or equal to, then follows the true or the false connection. For each side the admin picks which of that step's outputs to read, as described under chosen output. An optional amount is added to the second answer before the check, so "more than" with an amount of 5 is true only when the first answer exceeds the second by more than 5, and a blank amount adds nothing. The second step may be left empty, and the first answer is then checked against the amount alone, or against 0 when the amount is blank too. When either answer is missing or is not a number, the step decides false.
|
|
74
|
+
- **All of or Any of** — a built-in branching step that holds a list of comparisons and a "Must hold" setting of "all of" or "any of", then follows the true or the false connection. Each comparison names an earlier step and which of its outputs to read, then more than, less than, at least, at most or equal to, then an amount, a second step with its output, or both. A comparison with a second step holds the same way a Compare two values step would decide, adding the amount to the second answer, and one with no second step checks the first answer against the amount alone. "All of" decides true only when every comparison holds, and "any of" when at least one does. A comparison whose answer is missing or is not a number does not hold, and a step with no comparisons decides false.
|
|
74
75
|
- **Completion time** — the time a step was completed, kept for every visit beside the value that visit recorded. Every step that records a value has one, including steps that act on their own and waiting steps, not only steps a visitor answers. A stored run saves it with the run. A flow that carries its answers in the page carries each completion time in the page too, gives a value sent without a time the time it arrived, and drops a time it cannot read. A run stored when such a flow finishes keeps the completion times of the visits on its path. Going back discards the last visit's completion time with its value.
|
|
75
76
|
- **Completion time outputs** — three outputs every step that records a value offers beside its own: "Hour completed", the hour from 0 to 23 in the app's time zone, "Weekday completed", from 1 for Monday to 7 for Sunday, and "Minutes since 1970 when completed", the whole minutes since the start of 1 January 1970 in UTC. Each is read from the completion time of the latest visit to that step, and a step with no completion time has none of them. Compare, Compare two values and All of or Any of read them, so Compare two values with "more than" and an amount of 30 on two steps' minutes is true when the first step was completed more than 30 minutes after the second. Condition and Count list them among a step's outputs but do not read the time when one is picked.
|
|
76
77
|
- **Wait** — a built-in waiting step that holds a run until a time. The admin picks the earlier step it counts from and gives a number of minutes after it, a time of day as HH:MM, or a date as YYYY-MM-DD. A date holds the run until the start of that day in the app's time zone and wins over the other two, and it holds even when the earlier step has no completion time. Otherwise a time of day holds the run until the next time that time of day comes after the earlier step's completion time, the same day when it is still to come and the next day when it is not, and it wins over minutes. With neither, the run is held for the minutes after the earlier step's completion time, and blank minutes hold it not at all. While the earlier step has no completion time and no date is set, the run stays held. It is counted from the completion time of the latest visit to that step. Like any waiting step, once its time has come the run moves past it the next time the visitor's page is loaded or the app moves the run on.
|
|
77
|
-
- **Wait step name** — what a Wait step is called on its canvas card and in the "Waiting for" line a visitor sees, following the same order as the rule that holds the run. A date set names it by that date, such as "2026-12-01". Otherwise it is named by the time of day, or by the minutes followed by "minutes", then "after" and the earlier step, such as "09:00 after signup" or "30 minutes after signup". A Wait step with no date, time of day or minutes has no name of its own, so its canvas card shows the step's id and the visitor sees "Waiting for Wait".
|
|
78
|
-
- **Count** — a built-in step that acts on its own and records a whole number: how many visits to an earlier step the run has recorded. When the admin picks an answer, only visits that gave that answer are counted, and when the admin picks none, every visit is counted.
|
|
78
|
+
- **Wait step name** — what a Wait step is called on its canvas card and in the "Waiting for" line a visitor sees, following the same order as the rule that holds the run. A date set names it by that date, such as "2026-12-01". Otherwise it is named by the time of day, or by the minutes followed by "minutes", then "after" and the earlier step, such as "09:00 after signup" or "30 minutes after signup", with "minute" in place of "minutes" when the number is 1, such as "1 minute after signup". A Wait step with no date, time of day or minutes has no name of its own, so its canvas card shows the step's id and the visitor sees "Waiting for Wait".
|
|
79
|
+
- **Count** — a built-in step that acts on its own and records a whole number: how many visits to an earlier step the run has recorded. When the admin picks an answer, only visits that gave that answer are counted, and when the admin picks none, every visit is counted. The admin picks which of the earlier step's outputs the answer is read from, as described under chosen output. On the canvas the step is named "Count" followed by the earlier step and the answer it counts, such as "Count tests failed". Each visit to a Count step in a loop records a new count under its own visit key, so a Compare step after it can end the loop once the count reaches an amount.
|
|
79
80
|
- **Flow step** — a built-in step that names another flow and the number of one of its versions. The admin picks the flow from the same host's flows, listed by title as the admin's list of flows shows them. The step has one connection for each different output the End steps of that version write, and none when no flow or version is picked or no End step there has an output. On the canvas its card is named after the flow's title and the version, such as "Onboarding, version 2", and is marked "Runs a flow". Once a flow is picked, clicking the card opens that flow's canvas, and the card's Settings button opens the step's own settings instead. A canvas opened from a Flow step's card shows a "Back to" button named after the title of the flow it was opened from, or its slug when it has no title, which returns to that flow's canvas. The button only appears when that flow belongs to the same host, and to the same owner when the host has one. While the admin is connecting steps, clicking the card picks it as the connection's target. A run that reaches a Flow step stops there. When the run is a stored run, it starts one inner run of the named flow on the picked version. A run that carries its answers in the page, and an admin's preview, stop at the Flow step, start no run, and never move past it.
|
|
80
81
|
- **Unrunnable Flow step** — a Flow step whose picked flow or version cannot be run. Its card shows "missing flow" when no flow is picked or the picked flow no longer exists, and "unrunnable version" when the picked flow has no version with that number or that version is a draft, retired or withdrawn. These are always on, unlike the optional checks below, and the flow holding such a step cannot be saved as a new version or published until it is fixed.
|
|
81
82
|
- **Circular Flow step** — a Flow step that leads back to the flow it sits in. That is either a Flow step naming its own flow, or one naming a flow whose picked version holds a Flow step that leads back the same way, through any number of flows. Each flow along the way is read at the version its Flow step picked. Its card shows "circular", and like an unrunnable Flow step this is always on and keeps the flow from being saved as a new version or published until it is fixed.
|
|
82
83
|
- **Flow-starting step type** — a step type of the app's own that starts another flow the way the Flow step does. By default it runs the flow and version named in its settings called flow and version. It may instead carry a rule that works out the flow and version from its other settings, and that choice is then used everywhere a Flow step's choice is: starting the inner run, the unrunnable and circular checks, and the canvas link to that flow. When the step type gives the step no name of its own, the step is named after the flow and version that rule works out, the same way a Flow step is, such as "Onboarding, version 2", on its canvas card and in the "Waiting for" line a visitor sees. Everything said below about a Flow step's inner run and handing back applies to it too, except where a run-chosen flow says otherwise.
|
|
83
84
|
- **Run-chosen flow** — a flow-starting step type of the app's own that chooses the flow and version from the run it is in rather than from its settings, so two runs of one flow can start two different inner flows from the same step. Where there is no run, such as on the canvas, it has no chosen flow. Its steps are left out of the unrunnable and circular checks, so a flow holding one can be published with no flow in its settings. A run that reaches it and has no flow chosen fails with an error naming the step. When its inner run ends with no output, the step records `ended` and its run moves on.
|
|
84
85
|
- **Inner run** — the run a Flow step starts. It records which run started it and at which step, and that run holds at most one inner run for each Flow step, however often it is moved on. It belongs to the same owner as the run that started it. When the picked version has been retired or withdrawn, no inner run is started and the run that reached the Flow step fails with an error naming that step. While the run that started it is stopped at the Flow step, that run can name the inner run it is waiting on, and it names none when it is stopped at any other step or has finished. Its steps that act on their own run as soon as it starts, and it stops at its first step that asks for input or waits.
|
|
85
|
-
- **Handing back** — what happens when an inner run ends. Its flow output is recorded in the run that started it, as that run's answer at the Flow step, and a background job then moves that run on along the connection labelled with the output. An inner run that ended with no output records nothing in the run that started it, and the background job then fails with an error naming the Flow step, so that run does not move on. An output is handed back only once, so an inner run moved on again after it ended changes nothing. Because the move happens in a background job, the app must run its jobs for a run to go past a Flow step.
|
|
86
|
+
- **Handing back** — what happens when an inner run ends. Its flow output is recorded in the run that started it, as that run's answer at the Flow step, and a background job then moves that run on along the connection labelled with the output. An inner run that ended with no output records nothing in the run that started it, and the background job then fails with an error naming the Flow step, so that run does not move on. An inner run that a loop stopped hands nothing back and starts no background job, so the run that started it stays stopped at the Flow step and still names that inner run as the one it is waiting on. An output is handed back only once, so an inner run moved on again after it ended changes nothing. Because the move happens in a background job, the app must run its jobs for a run to go past a Flow step.
|
|
86
87
|
- **Waiting step** — a step whose type carries a rule saying whether what it waits for has happened, such as a payment clearing or a document being signed. A run that reaches it stops there, and the visitor sees "Waiting for" followed by the step's name, with no form to submit. Once the rule says it has happened, the run moves past the step the next time the visitor's page is loaded or the app moves the run on, and the step is recorded as `true`.
|
|
87
88
|
- **Check again** — a button a visitor sees under the "Waiting for" line in a flow that carries its answers in the page. It loads the step page again with the answers and their completion times, so the run moves past a waiting step whose rule now says it has happened, such as a Wait step whose time has come. A stored run shows no such button, since loading its page again does the same.
|
|
88
89
|
- **Registry** — the list of step types the app has registered, alongside the built-in ones. A step whose type is not registered is not shown or run as that type, and its answers are not checked.
|
|
@@ -96,7 +97,7 @@ easy_flow declares no commands of its own for this local. Its surface is split b
|
|
|
96
97
|
- **Answers** — the recorded values of a run, keyed by visit. The next step is always worked out from these values, their completion times and the connections in the pinned version.
|
|
97
98
|
- **Loop** — a connection that leads back to a step the run has already passed, so that step is visited again. Branching steps read the most recent answer to a step, so a loop ends when an answer sends the run down a different connection. Reading a finished run again follows the same connections it took at the time, since each branching step reads the answers as they stood at that visit.
|
|
98
99
|
- **Visit** — one time a run reaches a step. The first visit is keyed by the step id alone, and each later visit is keyed by the step id, an `@`, and the visit number, such as `size@2`. Every visit's value and completion time are kept, and a finished run lists each one.
|
|
99
|
-
- **Stopped loop** — a run that comes back to a step with no new answer given since its last visit there ends at that point rather than cycling forever. Only answers the visitor gives count, so a loop made only of steps that act on their own stops the first time it comes round. A stored run stopped this way names the step the loop came back to, and does not count as having ended at an End step.
|
|
100
|
+
- **Stopped loop** — a run that comes back to a step with no new answer given since its last visit there ends at that point rather than cycling forever. Only answers the visitor gives count, so a loop made only of steps that act on their own stops the first time it comes round. A stored run stopped this way names the step the loop came back to, and does not count as having ended at an End step. An inner run stopped this way leaves the run that started it at its Flow step, as described under handing back.
|
|
100
101
|
- **Ended at an End step** — whether a stored run's path finished on an End step, or on a step of the app's own type that ends a flow. It does not while the run is still going or held at a step, when a loop stopped it, or when it stopped at a step with no connection to follow.
|
|
101
102
|
- **Reserved `@`** — a step id may not contain `@`, since that mark numbers later visits. A flow holding such a step id is reported as invalid.
|
|
102
103
|
- **Refused answer** — an answer the step's answer check turned down. Nothing is recorded for that step, and the visitor is shown the same step again with the check's message. This works the same whether the flow keeps a stored run or carries its answers in the page.
|
|
@@ -105,6 +105,8 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
105
105
|
- After install, check that a Condition step placed on the canvas, once its earlier step is picked, offers that step's outputs, a comparison of "is" or "is not", and the answers of the output picked. It is named after that step, the comparison and the answer, decides a true or false result, and needs no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
106
106
|
- After install, check that a Count step placed on the canvas, once its earlier step and answer are picked, is named "Count" followed by that step and that answer. It needs no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
107
107
|
- After install, check that a Compare two values step placed on the canvas offers the outputs of each earlier step picked for its two sides, a comparison of "more than", "less than", "at least", "at most" or "equal to", and an amount added to the second side. It decides a true or false result and is connected onward once for each. It needs no migration and no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
108
|
+
- After install, check that a Compare two values step with its second step left empty can be saved and published, and that it compares the first side's output with the amount alone.
|
|
109
|
+
- After install, check that a Compare step, a Count step, a Compare two values step, or a comparison of an All of or Any of step that names an earlier step with no output chosen shows "missing setting" in red on its canvas card, and that saving a version or publishing is refused until an output is chosen. This check is always on and is not turned on with `EasyFlow.check`.
|
|
108
110
|
- After publishing a flow with a Compare two values step, check that a run whose answer on either side is not a number follows the connection for a false result.
|
|
109
111
|
- After install, check that an All of or Any of step placed on the canvas asks whether "all of" or "any of" its comparisons must hold, and for a list of comparisons. Each comparison offers the outputs of the earlier step picked for it, a comparison of "more than", "less than", "at least", "at most" or "equal to", an amount, and an optional second step with its output. With no second step, the output is compared with the amount alone. The step decides a true or false result and is connected onward once for each. It needs no migration and no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
110
112
|
- After publishing a flow with an All of or Any of step, check that a run follows the connection for a false result when the step holds no comparisons, and that a comparison whose answer is not a number counts as not holding.
|
|
@@ -117,6 +119,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
117
119
|
- When a stored run reaches a Flow step, the engine starts a run of the chosen flow and records the run it was started from. That record needs the migration that adds a parent run to the runs table, so an app upgrading from a version without it runs `bin/rails easy_flow:install:migrations` and `bin/rails db:migrate` before publishing a flow with a Flow step.
|
|
118
120
|
- After publishing a flow with a Flow step, check that when a visitor finishes the chosen flow, the run that holds the Flow step moves on along the connection named after the output that flow ended with. If it stays at the Flow step, check the queue adapter from step 12.
|
|
119
121
|
- When a chosen flow ends at an End step with no output filled in, the engine records nothing at the Flow step, and the job queued on `default` fails with `EasyFlow::InnerFlowError` naming the Flow step. The run that holds the Flow step stays at it. Check that the queue adapter from step 12 keeps or reports failed jobs, and that every End step of a flow chosen on a Flow step has an output.
|
|
122
|
+
- When a loop stops a run started by a Flow step, or by a step type of the app's own that starts a flow, the engine records nothing at the step that started it and queues no job. A loop stops a run when it comes back to a step with no answer given since the last visit to that step. The run that holds the starting step stays at it and no error is raised. This holds for a step type of the app's own that chooses the flow from the run as well, so it records no `ended` answer either. Check that every loop in a flow chosen on a Flow step passes a step that waits for the visitor's answer.
|
|
120
123
|
- When a run reaches a Flow step whose chosen version was retired or withdrawn after the flow was published, the engine raises `EasyFlow::InnerFlowError` naming the Flow step and starts no run. The engine does not turn this error into a refusal, so the visitor gets the app's error page. Check that the app's error reporting captures it.
|
|
121
124
|
- After install, check that removing a flow from `<mount path>/manage/flows` shows "Flow removed." and takes the flow off the list. Removing a flow deletes its versions and its runs, and needs no setting in the initializer.
|
|
122
125
|
- After publishing a flow with a Flow step, check that while a run of it is stopped at that Flow step, removing the chosen flow is refused and the flow list shows "This flow cannot be removed while" followed by the waiting flow's title, or its slug when it has no title, and "waits on one of its runs". If no message is shown, check that the admin layout shows `flash[:alert]`.
|
|
@@ -126,7 +129,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
126
129
|
- Every stored run keeps each step's value together with the time the step was completed. An app upgrading from a version that kept values alone runs `bin/rails easy_flow:install:migrations` and `bin/rails db:migrate` before any stored run takes an answer. The migration wraps each value its existing runs already hold, and those values carry no completion time. Running the migration back drops every completion time.
|
|
127
130
|
- After install, check that a Compare step, a Compare two values step, or a comparison of an All of or Any of step, once its earlier step is picked, offers "Hour completed", "Weekday completed" and "Minutes since 1970 when completed" beside that step's own outputs. They need no setting in the initializer.
|
|
128
131
|
- After install, check that a Wait step placed on the canvas asks for the earlier step it counts from, minutes after it, a time of day as HH:MM and a date as YYYY-MM-DD. It needs no migration beyond the one that keeps each step's completion time with its value and no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
129
|
-
- After filling in a Wait step, check its name on the canvas. With a date set it is named by the date alone, and the run waits until that date. Otherwise, with a time of day set, it is named by that time followed by "after" and the step it counts from, and the run waits until that time of day first comes after the step was answered. Otherwise it is named by the minutes, as "<n> minutes after" followed by the step it counts from, and the run waits that many minutes past the answer. With none of the three set it has no name of its own, and a visitor held at it sees "Waiting for Wait."
|
|
132
|
+
- After filling in a Wait step, check its name on the canvas. With a date set it is named by the date alone, and the run waits until that date. Otherwise, with a time of day set, it is named by that time followed by "after" and the step it counts from, and the run waits until that time of day first comes after the step was answered. Otherwise it is named by the minutes, as "1 minute after" or "<n> minutes after" followed by the step it counts from, and the run waits that many minutes past the answer. With none of the three set it has no name of its own, and a visitor held at it sees "Waiting for Wait."
|
|
130
133
|
- The engine queues no job for a Wait step. After publishing a flow with a Wait step, check that a visitor held at it sees "Waiting for" followed by the step's name, and that a stored run moves on when the visitor's page is loaded after its time has come. Moving a run on with no visitor present belongs to the `easy_flow-develop` local.
|
|
131
134
|
- After publishing a flow with a Wait step whose "What this keeps of a run" detail is "Nothing" or "The whole run once it finishes", check that a visitor held at it sees a "Check again" button, and that pressing it after the time has come carries their answers and completion times forward and moves them past the Wait step.
|
|
132
135
|
- After setting `EasyFlow.file_store`, publish a flow with a File upload step whose "What this keeps of a run" detail is "Every answer as it is given", upload a file as a visitor, and check that the completion page shows the name the store gives it.
|