easy_flow 0.20.0 → 0.22.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f82c451744e7908c5f98dc0398363d373894a2635280494a7939186373cbe27
4
- data.tar.gz: 393c50e4c09189c33f1ff57f64fc2b5c3e5d13b40d0ebca204ffbf5d620c6418
3
+ metadata.gz: 8c1b56b170c92f6fa9d7c2edc9864de42dfaef4dc97d41822442ca289722a91e
4
+ data.tar.gz: b4aa7640c10adcac242e412837870517ad688c463624d7f53dea366e4b6d0e96
5
5
  SHA512:
6
- metadata.gz: 021def99dc4c41c8ec14d6ea64b55c42d02d683336119822e5eaa326b31f3781dd84b3e7df6bd48faf59b89adbb3de7cfdf6f844cf2bf832639f408a1dfa7c36
7
- data.tar.gz: 0365723b91965fe7a42b2a57cc906be4881cbb78c40420879c1245035fb30a0b7162252b31c81d975161d302404998e7de6e459323219174f247fc1a5fa49d80
6
+ metadata.gz: 6a9ea816b147d6782cdc106113aeae7d95b0396679f920203afafad68f130a5895efb9ba0a020a7c55438260c2d8a782d58399a2aefd97574467be10255b5fe8
7
+ data.tar.gz: 1084833772e7cea7306b369543e362d46fa552222bb398ae1bd0edc7d52aa4d7bc20ddeaac3021f301d2b9e65c662e195a489e96d09653b3b54840ffd345f364
@@ -45,7 +45,15 @@ module EasyFlow
45
45
  end
46
46
 
47
47
  def run_location(run)
48
- flow_routes.run_path(run)
48
+ flow_routes.run_path(run_address(run))
49
+ end
50
+
51
+ def run_address(run)
52
+ run
53
+ end
54
+
55
+ def find_run(runs, address)
56
+ runs.find(address)
49
57
  end
50
58
 
51
59
  def start_run(flow)
@@ -101,7 +109,7 @@ module EasyFlow
101
109
  end
102
110
 
103
111
  def admitted_run
104
- found = Run.where(flow: hosted_flows).find(params[:id])
112
+ found = find_run(Run.where(flow: hosted_flows), params[:id])
105
113
 
106
114
  Admission.of_run(found, permitted: permitted?(found.flow))
107
115
  end
@@ -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
- required.reject { |name| entry.to_h[name.to_s].present? }
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
 
@@ -1,3 +1,3 @@
1
1
  module EasyFlow
2
- VERSION = "0.20.0"
2
+ VERSION = "0.22.0"
3
3
  end
@@ -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
 
@@ -21,6 +21,8 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
21
21
  - `EasyFlow::FlowsController` — the visitor controller; the app subclasses it to serve one host's flows from its own routes and to change what happens at the start, on each step and at the finish.
22
22
  - `hosted_by` — class method on a `FlowsController` subclass naming the host whose flows it serves.
23
23
  - `routed_by` — class method on a `FlowsController` subclass naming the prefix of the app's own route names that the controller redirects and links to.
24
+ - `run_address` — private method a `FlowsController` subclass overrides to give the value put in a run's address in place of its id.
25
+ - `find_run` — private method a `FlowsController` subclass overrides to find a run, among the host's own runs, from the value in its address.
24
26
  - `EasyFlow::Run` — the stored record of one visitor's pass through a flow, started with `EasyFlow::Run.start` on the live version or a chosen published one and pinned to that version, moved on past a waiting step with `advance`, read for the output its flow ended with by `output`, for whether it ended at an End step by `ended_at_end?`, for the step a loop stopped it at by `stopped_on_loop`, for when each step visit was completed by `completed_at` and for the time a Wait step holds it until by `held_until`, and linked to the runs its Flow steps started by `inner_runs`, to the one it is waiting on by `waiting_on`, and to the run that started it by `parent_run`.
25
27
  - `EasyFlow::QuestionRunner` — reads a flow document: its steps, the next step for a set of answers and the times their steps were completed, the answers on the path taken, and a question's text and an answer's label.
26
28
 
@@ -79,7 +81,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
79
81
  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
82
  5. Use these words to declare the type. Each is called once at class level (or inside the `EasyFlow.step` block):
81
83
  - `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 always required. `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`. `: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".
84
+ - `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
85
  - `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
86
  - `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
87
  - 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.
@@ -330,6 +332,7 @@ Use this when an admin should edit a value that belongs to one of the app's own
330
332
  - `finished(answers, run)` — called when the visitor reaches the end. `answers` is the answers on the path taken, keyed by step id as symbols, each a string or, for a checklist, an array of strings. `run` is the stored `EasyFlow::Run`, or `nil` when the admin set the flow to save nothing. A flow the admin set to save on finish gets its run created at this point. To act on which way the flow ended, read `run.output`; when `run` is `nil` there is no output to read. It must render or redirect. Default: the engine's completion page, which lists each answer on the path taken by its label, a checklist's answer as the labels of everything ticked joined into one sentence, and a File upload step's answer as the name the host's file store gives the file.
331
333
  - `start_run(flow)` — creates the run when a visitor starts a flow the admin set to save each step. Call `super` and change the run it returns, for example to set its `owner` or `label`. To start the run on a version other than the live one, return `EasyFlow::Run.start(flow, version: ...)` instead of calling `super`; see "Start a run on a chosen version".
332
334
  - `runner_for(definition)` — returns the runner used for each step. Return a subclass of `EasyFlow::QuestionRunner` to change what the step and completion pages read from it, built with `host: flow_host`, such as `IntakeRunner.new(definition, host: flow_host)`. A runner built without `host:` ignores the host's `allowed_answers`, so every step offers every answer and none is filled in or skipped.
335
+ - `run_address(run)` and `find_run(runs, address)` — name a run in the controller's own addresses by something other than its id, such as a random code kept on one of the app's own records. `run_address(run)` returns the value put in the run's address, and is used for the address a visitor is sent to after starting a run or answering a step and for the step form's address. `find_run(runs, address)` returns the run that address names, found within `runs`, which holds only runs of the host's own flows, and raises `ActiveRecord::RecordNotFound` when there is none. Override both together. Default: the run's id, and `runs.find(address)`.
333
336
  5. Visit `/<path>/<slug>` for a published flow and walk it to the end.
334
337
 
335
338
  ### Start a run on a chosen version
@@ -483,7 +486,7 @@ Use this when a visitor's run should stop until a set time has passed, such as a
483
486
  - `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.
484
487
  - `Date, as YYYY-MM-DD` — the run is held until the start of that date in the app's time zone.
485
488
  - 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.
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`. 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.
489
+ - 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.
487
490
  3. What then holds:
488
491
  - 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.
489
492
  - 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.
@@ -549,6 +552,7 @@ Use this when a visitor's run should stop until a set time has passed, such as a
549
552
  - A runner the app builds without `host:` ignores the host's `allowed_answers`.
550
553
  - 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.
551
554
  - 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.
555
+ - 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.
552
556
  - A type that routes declares the values its output takes, or the canvas offers no connections to label.
553
557
  - 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.
554
558
  - A run starts only on a version its own flow has published and not retired or withdrawn, and any other version raises `ActiveRecord::RecordInvalid`.
@@ -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, 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.
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,14 +69,14 @@ 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. When the earlier step recorded several outputs, the admin picks which one. An answer that is missing or is not a number decides false.
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, when that earlier step recorded several outputs, the admin picks which one. 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. When either answer is missing or is not a number, the step decides false.
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, when that step recorded several outputs, which one, 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.
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. When the earlier step recorded several outputs, the admin picks which one the answer is read from. 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.
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.
@@ -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.
@@ -127,7 +129,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
127
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.
128
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.
129
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`.
130
- - 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."
131
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.
132
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.
133
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.
@@ -19,6 +19,8 @@ develop:
19
19
  - EasyFlow::FlowsController
20
20
  - hosted_by
21
21
  - routed_by
22
+ - run_address
23
+ - find_run
22
24
  - EasyFlow::Run
23
25
  - EasyFlow::QuestionRunner
24
26
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tylercschneider