easy_flow 0.10.0 → 0.12.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/assets/builds/easy_flow/canvas.js +1 -1
- data/app/controllers/concerns/easy_flow/manage/draws_canvas.rb +1 -1
- data/app/controllers/easy_flow/manage/canvas_controller.rb +2 -2
- data/app/controllers/easy_flow/manage/flows_controller.rb +4 -1
- data/app/javascript/easy_flow/canvas/Canvas.jsx +2 -0
- data/app/javascript/easy_flow/canvas/Control.jsx +1 -1
- data/app/javascript/easy_flow/canvas/StepCard.jsx +5 -2
- data/app/jobs/easy_flow/advance_parent_job.rb +9 -0
- data/app/models/easy_flow/canvas.rb +25 -4
- data/app/models/easy_flow/chosen_flow.rb +26 -0
- data/app/models/easy_flow/definition.rb +10 -0
- data/app/models/easy_flow/flow_step.rb +27 -0
- data/app/models/easy_flow/progress/kept.rb +17 -0
- data/app/models/easy_flow/progress/loose.rb +4 -0
- data/app/models/easy_flow/run.rb +17 -2
- data/app/models/easy_flow/runner.rb +6 -0
- data/app/models/easy_flow/step_type/declaration.rb +10 -1
- data/app/models/easy_flow/step_type.rb +17 -2
- data/app/models/easy_flow/validator.rb +37 -2
- data/app/views/easy_flow/manage/flows/show.html.erb +4 -0
- data/db/migrate/20261007120000_add_parent_run_to_easy_flow_runs.rb +6 -0
- data/lib/easy_flow/engine.rb +1 -0
- data/lib/easy_flow/version.rb +1 -1
- data/lib/easy_flow.rb +2 -1
- data/the_local/agents/easy_flow-develop.md +146 -10
- data/the_local/agents/easy_flow-info.md +16 -7
- data/the_local/agents/easy_flow-install.md +20 -6
- data/the_local/interface.yml +2 -0
- metadata +5 -1
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
<%= ui_button(label: "Try it", href: flow_routes.manage_flow_preview_path(@flow), variant: :secondary) %>
|
|
18
18
|
|
|
19
|
+
<% if @parent %>
|
|
20
|
+
<%= ui_button(label: "Back to #{@parent.title.presence || @parent.slug}", href: flow_routes.manage_flow_path(@parent), variant: :secondary) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
19
23
|
<%= ui_button(label: "All flows", href: flow_routes.manage_flows_path, variant: :secondary) %>
|
|
20
24
|
</div>
|
|
21
25
|
</header>
|
data/lib/easy_flow/engine.rb
CHANGED
data/lib/easy_flow/version.rb
CHANGED
data/lib/easy_flow.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "easy_flow/host_routes"
|
|
|
6
6
|
require "easy_flow/engine"
|
|
7
7
|
|
|
8
8
|
module EasyFlow
|
|
9
|
-
FIELD_TYPES = %i[string integer float boolean select multi_select previous_step from_step list].freeze
|
|
9
|
+
FIELD_TYPES = %i[string integer float boolean select multi_select previous_step from_step list flow].freeze
|
|
10
10
|
|
|
11
11
|
OUTPUT_TYPES = %i[string integer float boolean].freeze
|
|
12
12
|
OPTIONAL_CHECKS = %i[unrouted_value unfollowed_path dead_end].freeze
|
|
@@ -19,6 +19,7 @@ module EasyFlow
|
|
|
19
19
|
class NotPublished < StandardError; end
|
|
20
20
|
class NotPermitted < StandardError; end
|
|
21
21
|
class OutOfService < StandardError; end
|
|
22
|
+
class InnerFlowError < StandardError; end
|
|
22
23
|
class Withdrawn < StandardError; end
|
|
23
24
|
|
|
24
25
|
DRAWING = "easy_flow/steps/choosing".freeze
|
|
@@ -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, picks the next branch, or holds the visitor until something outside the flow has happened), 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 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, acting when a visitor finishes a flow, acting on the output a visitor's flow ended with, 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, branching logic, number comparisons, hard-coded setting options, copying a record's value into a flow and back, answer validation, polling or "come back later" pages, flow controllers or answer lookups.
|
|
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, picks the next branch, or holds the visitor until something outside the flow has happened), 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, 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, 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 or whose flow ended with no output, 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, branching logic, number comparisons, hard-coded setting options, copying a record's value into a flow and back, answer validation, 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
|
|
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 eight: 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), three that pick a branch from an earlier answer with no code — condition (`:condition`, is or is not a chosen value), switch (`:switch`, follows the connection labelled with the answer) and compare (`:compare`, reads the answer as a number and checks it by more than, less than, at least or at most against an amount) — 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. Before writing a step type to ask for one answer or several, or to branch on an answer or a number, ask the developer whether an admin placing one of the built-in types on the canvas is enough. None of condition, switch or compare 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, 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
|
|
|
@@ -18,7 +18,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
18
18
|
- `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.
|
|
19
19
|
- `hosted_by` — class method on a `FlowsController` subclass naming the host whose flows it serves.
|
|
20
20
|
- `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.
|
|
21
|
-
- `EasyFlow::Run` — the stored record of one visitor's pass through a flow,
|
|
21
|
+
- `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`, 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`.
|
|
22
22
|
- `EasyFlow::QuestionRunner` — reads a flow document: its steps, the next step for a set of answers, the answers on the path taken, and a question's text and an answer's label.
|
|
23
23
|
|
|
24
24
|
## How to use it
|
|
@@ -31,7 +31,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
31
31
|
- It only picks which connection to follow — define `route`.
|
|
32
32
|
- It holds the visitor until something outside the flow has happened, such as a payment arriving or a reviewer approving — declare `waits_until`.
|
|
33
33
|
A type may both `process` and `route`. A type that does none of the four is passed through without stopping when a visitor reaches it.
|
|
34
|
-
2. Create the class in the app, for example `app/models/flow_steps/rating.rb`. The class name, underscored, is the type's id (`Rating` becomes `:rating`), and that id is stored in every flow that uses it, so it must not change after admins start using the type. It must not be one of the built-in ids `start`, `terminal`, `question`, `checklist`, `condition`, `switch` or `
|
|
34
|
+
2. Create the class in the app, for example `app/models/flow_steps/rating.rb`. The class name, underscored, is the type's id (`Rating` becomes `:rating`), and that id is stored in every flow that uses it, so it must not change after admins start using the type. It must not be one of the built-in ids `start`, `terminal`, `question`, `checklist`, `condition`, `switch`, `compare` or `flow_step`:
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
37
|
module FlowSteps
|
|
@@ -76,13 +76,53 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
76
76
|
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.
|
|
77
77
|
5. Use these words to declare the type. Each is called once at class level (or inside the `EasyFlow.step` block):
|
|
78
78
|
- `step_name "<label>"` — the name admins see on the canvas. Defaults to the id.
|
|
79
|
-
- `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`, 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`. `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".
|
|
79
|
+
- `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".
|
|
80
80
|
- `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.
|
|
81
81
|
- `names_by :setting` or `names_by { |node| ... }` — what the step is called on the canvas, from a setting or computed.
|
|
82
82
|
- `awaits_input` — the visitor is shown this step and submits an answer to it.
|
|
83
83
|
- `waits_until { |node, state| ... }` — the run stops at this step until the block returns a truthy value. See step 8.
|
|
84
84
|
- `answer_check { |node, value| ... }` — checks a submitted answer before it is recorded. `value` is the submitted string, an array of strings when the input submits several values, or `nil` or `""` when the input sent nothing. An array may hold blank strings. Return a message to refuse the answer, or `nil` to accept it. Without it every answer is accepted, including a blank one.
|
|
85
85
|
- `ends_here` / `begins_here` — marks the type as an end or a start of a flow. A run that ends on a step of an `ends_here` type gives that step's `output` setting as `run.output`, so a type of the app's own that ends a flow declares `setting :output, type: :string` for the admin to fill in, or gives no output.
|
|
86
|
+
- `starts_a_flow` — when a stored run stops at a step of this type, a run of another flow is started. Without `chooses_flow`, the type must declare `setting :flow, type: :flow` and `setting :version, type: :integer`, since the new run is started on that flow's version with that number. The new run is only started at a step the run stops at, so the type must also declare `waits_until` or `awaits_input`. When the new run ends, its output is recorded under this step's id, so a type that should follow a connection per output defines `route` returning `state[node.id]` and declares an `output` whose `values:` are the outputs the named flow can end with. Before writing such a type, ask the developer whether the built-in Flow step is enough. See "Follow a run into the flow a Flow step started".
|
|
87
|
+
- On the canvas, a step of the type is marked `Runs a flow`.
|
|
88
|
+
- Once the step's `flow` setting names a flow, clicking the step takes the admin to that flow's page, and a `Settings` button on the step opens the step's own settings. Any other `:flow` setting the type declares is not followed by the click and is not read when the run is started.
|
|
89
|
+
- A flow's page reached by clicking the step shows a `Back to <title>` button, or `Back to <slug>` when the flow it came from has no title, which returns the admin to the flow the step is in. The button is shown only when that flow belongs to the same host.
|
|
90
|
+
- While the step's `flow` setting is blank, clicking the step opens its settings like any other step.
|
|
91
|
+
- While an admin is connecting two steps, clicking the step picks it as the connection's target, the same as any other step.
|
|
92
|
+
- The step's card shows `missing flow` while its `flow` setting is blank or names no existing flow, and `unrunnable version` while the flow exists but its `version` setting is blank, names no version of that flow, or names one that is a draft, retired or withdrawn.
|
|
93
|
+
- The step's card shows `circular` while its `flow` setting names the flow the step sits in, or names a flow whose chosen version holds a `starts_a_flow` step that leads back to it the same way, through any number of flows. Each flow along the way is read at the version named in the `version` setting of the step that leads to it.
|
|
94
|
+
- While `missing flow`, `unrunnable version` or `circular` shows, saving the flow as a new version and publishing it are refused. These checks are always made and are not among the optional checks set through `easy_flow-install`, so the type needs no `required:` or `check:` on those two settings for them.
|
|
95
|
+
- `chooses_flow { |node| { flow: ..., version: ... } }` — for a `starts_a_flow` type, works out the flow and version a step runs from the step's own settings instead of reading the settings named `flow` and `version`. Ask the developer which settings decide the flow and version, and how. Do not pick.
|
|
96
|
+
- The block is given the node only. `node.config` holds the admin's settings with string keys, without any setting declared with `kept_on:`.
|
|
97
|
+
- It returns a hash with the symbol keys `flow:`, the flow's id as an integer or a string, and `version:`, the version's number. A blank value under either key is treated as not chosen.
|
|
98
|
+
- When the block returns `nil`, the step falls back to the settings named `flow` and `version`.
|
|
99
|
+
- What it returns is used everywhere the settings named `flow` and `version` are used otherwise: the run started when a stored run stops at the step, the `missing flow`, `unrunnable version` and `circular` marks and the refusal to save or publish, and the flow's page opened by clicking the step on the canvas.
|
|
100
|
+
- 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 host inside the block when it must be limited to one host or owner.
|
|
101
|
+
- A step of a type with no `names_by`, or whose `names_by` gives a blank, is named after the flow and version the block returns, as that flow's title and the version number, such as `Onboarding, version 2`, or `version 2` alone when the flow has no title. That name is shown on the canvas and in `Waiting for <name>.` while a run waits at the step. When the block returns `nil`, the step is not named after the settings named `flow` and `version`, and falls back to its id on the canvas and its `step_name` in the waiting message. Ask the developer whether that name is what admins and visitors should read, and declare `names_by` when it is not.
|
|
102
|
+
- The block is called each time the canvas draws the step, each time the step's name is shown, each time the flow is saved as a version or published, and when a run stops at the step, so it must not be slow and must not change anything.
|
|
103
|
+
- A type that routes on the new run's output declares an `output` whose `values:` are the outputs the chosen flow can end with, or the canvas offers no connections to label.
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
module FlowSteps
|
|
107
|
+
class Offer
|
|
108
|
+
include EasyFlow::Step
|
|
109
|
+
|
|
110
|
+
step_name "Offer"
|
|
111
|
+
|
|
112
|
+
setting :plan, type: :select, options: %w[basic premium], required: true
|
|
113
|
+
|
|
114
|
+
starts_a_flow
|
|
115
|
+
waits_until { |_node, _state| false }
|
|
116
|
+
chooses_flow { |node| OfferFlow.for_plan(node.config["plan"]) }
|
|
117
|
+
|
|
118
|
+
def route(node, state)
|
|
119
|
+
state[node.id]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Here `OfferFlow.for_plan` is the app's own lookup, returning `{ flow: <id>, version: <number> }` or `nil`.
|
|
86
126
|
- `displays_by { |node| ... }` or `displays_by { |node, run| ... }` — builds the object handed to the step's partial as the local `step`. Without it the partial receives the node itself. A block that takes two arguments is also given the visitor's stored `EasyFlow::Run`, which is `nil` when the flow keeps no stored run at this point and in an admin's preview, so the block must handle `nil`. The block is also called with a `nil` run once for each step already answered every time a step page is shown, to count progress, so it must not be slow and must not change anything.
|
|
87
127
|
- `drawn_by "<partial>"` — the partial that draws this type for a visitor. Without it the host's default drawing is used, which draws one answer to pick and expects `step.id`, `step.text` and `step.choices`, each choice answering `value`, `label`, `hint` and `info`, so a type with its own display shape needs its own partial.
|
|
88
128
|
6. For a type that computes or routes, define instance methods on the class (or `process { |node, state| ... }` / `route { |node, state| ... }` in the block form):
|
|
@@ -161,13 +201,13 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
161
201
|
|
|
162
202
|
- The block is given `node` and `state` only, the same as `process`, and is not given the run or its owner. Ask the developer what the step waits for, and how the block finds it from the step's settings and the answers recorded before it. If it cannot be found from those, stop and ask, since the block has nothing else to read.
|
|
163
203
|
- The block is called each time the visitor loads the step page, and each time the app calls `run.advance`. Keep it to a single lookup.
|
|
164
|
-
- While the block returns a falsy value, the visitor is shown `Waiting for <name>.` and no form, no Next button and no Back button. `<name>` is what `names_by` gives, or the `step_name` when there is
|
|
204
|
+
- While the block returns a falsy value, the visitor is shown `Waiting for <name>.` and no form, no Next button and no Back button. `<name>` is what `names_by` gives, or for a type that declares `chooses_flow` the chosen flow's title and version, or the `step_name` when there is neither, so give the type a name a visitor can read.
|
|
165
205
|
- Once the block returns a truthy value, `true` is recorded under the step's id, or the value `process` returns when the type also defines `process`, and the run goes on to the next step.
|
|
166
206
|
- Do not declare `awaits_input` on a type that waits. The waiting message replaces the form, so the input is never shown.
|
|
167
207
|
- The page does not reload itself. Ask the developer how the run should move on when what it waits for happens:
|
|
168
208
|
- The visitor reloads the page. This needs nothing more, and works whether or not the flow keeps a stored run.
|
|
169
209
|
- The app calls `run.advance` where the event is handled, such as a webhook or a job. See "Read a run and its answers". This needs a stored run, so it only applies to a flow the admin set to save each step.
|
|
170
|
-
9. Restart the server, open a flow on the canvas of each host that should offer the type, and check the type is in the palette, its settings show, and a preview walks through it. On a host that names a list of offered step types without the type's id, check the type is not in the palette.
|
|
210
|
+
9. Restart the server, open a flow on the canvas of each host that should offer the type, and check the type is in the palette, its settings show, and a preview walks through it. On a host that names a list of offered step types without the type's id, check the type is not in the palette. For a type that declares `starts_a_flow`, check the step is marked `Runs a flow`, that once a flow is picked in its settings clicking the step goes to that flow's page, that the `Back to` button on that page returns to the flow the step is in, and that its `Settings` button opens its settings. Also check that the step shows `missing flow` while no flow is picked, `unrunnable version` while the version number names no live or replaced version, `circular` while it names the flow it sits in, and that publishing is refused until each is fixed. For a type that declares `chooses_flow`, set the settings the block reads and check that clicking the step goes to the flow the block names, that a type with no `names_by` is named on the canvas by that flow's title and version, and that a run stopping at the step starts a run of that flow on that version.
|
|
171
211
|
|
|
172
212
|
### Keep a setting on one of the app's records
|
|
173
213
|
|
|
@@ -222,10 +262,88 @@ Use this when an admin should edit a value that belongs to one of the app's own
|
|
|
222
262
|
The controller inherits from the base controller set at install, uses the host's layout, visitor authorization and refusal methods, and only finds flows that belong to the named host. Pages it does not override use the engine's own views.
|
|
223
263
|
4. Override only the private methods the developer's answers call for:
|
|
224
264
|
- `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.
|
|
225
|
-
- `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`.
|
|
265
|
+
- `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".
|
|
226
266
|
- `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.
|
|
227
267
|
5. Visit `/<path>/<slug>` for a published flow and walk it to the end.
|
|
228
268
|
|
|
269
|
+
### Start a run on a chosen version
|
|
270
|
+
|
|
271
|
+
Use this when a visitor's run should follow a published version of a flow other than the live one, such as the version an earlier run of the same owner was on.
|
|
272
|
+
|
|
273
|
+
1. Ask the developer which version the run should start on and how the app knows it, such as a version number stored on the owner or read from the request. Do not pick it.
|
|
274
|
+
2. Find the version among the flow's own versions, for example `flow.definition_versions.find_by(number: number)`. A version answers `number`, and `live?` or `superseded?` when it is published.
|
|
275
|
+
3. Start the run with `EasyFlow::Run.start(flow, version: version)`. Without `version:` the run starts on the flow's live version. Either way the run is pinned to that version, and publishing another version later does not change it.
|
|
276
|
+
4. The version must be one the flow has published and not taken out of service, which is the live version or one it replaced. A version that is a draft, is retired or withdrawn, belongs to another flow, or is `nil` makes `start` raise `ActiveRecord::RecordInvalid` and no run is created. Ask the developer what the visitor is shown when that happens, such as starting on the live version instead or refusing.
|
|
277
|
+
5. To do this when a visitor starts a flow from the app's own controller, override `start_run(flow)` and return the run:
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
def start_run(flow)
|
|
281
|
+
version = flow.definition_versions.find_by(number: current_customer.intake_version)
|
|
282
|
+
EasyFlow::Run.start(flow, version: version || flow.live_version).tap { |run| run.update!(owner: current_customer) }
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
This only applies to a flow the admin set to save each step. A flow set to save on finish, or to save nothing, always runs on the live version.
|
|
287
|
+
6. Start a run on an older published version, walk it, and check the steps shown are that version's. Start one on a draft and check it is refused.
|
|
288
|
+
|
|
289
|
+
### Follow a run into the flow a Flow step started
|
|
290
|
+
|
|
291
|
+
Use this when a visitor's run reaches a Flow step and the app must take the visitor into the flow it names, or read the run it started.
|
|
292
|
+
|
|
293
|
+
1. This needs the engine's migrations installed, which `easy_flow-install` does.
|
|
294
|
+
2. What holds when a stored run stops at a Flow step, whether on the visitor's page load or on `run.advance`:
|
|
295
|
+
- A run of the flow named in the step's settings is started on the version with the number in its settings, or of the flow and version its type's `chooses_flow` block returns. The flow is found by its id alone and not through the host, since a `:flow` setting is not checked on save.
|
|
296
|
+
- The new run's `parent_run` is the run that reached the step, and its `parent_step` is the step's id as a string, or the visit key such as `"sub@2"` on a later visit in a loop.
|
|
297
|
+
- The new run is moved on at once past every step that acts without the visitor, the same as `run.advance`. When it stops at a Flow step of its own, that starts a further run the same way.
|
|
298
|
+
- It is started once per visit to the step, so loading the page again or calling `advance` again starts no second one.
|
|
299
|
+
- Its `owner` is the parent run's `owner` at the moment the new run is started, or blank when the parent run has none. Setting or changing the parent run's owner later does not change it. Its `label` and `status` are not copied and are left blank.
|
|
300
|
+
- The parent run shows the visitor `Waiting for <name>.`, where the built-in Flow step's name is the named flow's title and version number, such as `Waiting for Intake, version 2.`, with no form, no Next button and no Back button, and stays on the Flow step until the new run ends.
|
|
301
|
+
- A flow set to save on finish or to save nothing, and an admin's preview, keep no stored run, so they start no run at a Flow step.
|
|
302
|
+
- A flow cannot be published while one of its Flow steps names a missing flow, a version that cannot run, or a flow that leads back to it. A flow or version can still stop being runnable after the flow holding the step was published, such as a named flow being deleted or its version retired or withdrawn. Then, at the moment the run stops at the Flow step, one of these is raised and no run is started:
|
|
303
|
+
- `ActiveRecord::RecordNotFound` when the named flow is gone.
|
|
304
|
+
- `EasyFlow::InnerFlowError` when the named version is retired or withdrawn, with a message naming the Flow step's id, such as `The Flow step sub names a version that cannot run`.
|
|
305
|
+
- `ActiveRecord::RecordInvalid` when the version number is blank, names no version of the flow, or names a draft.
|
|
306
|
+
3. What holds when the new run ends with an output, whether on its visitor's page load or on `inner.advance`:
|
|
307
|
+
- The new run's `output` is recorded in the parent run under its `parent_step`, so `run.recorded[:<step id>]` holds the text the admin wrote on the End step the new run ended on.
|
|
308
|
+
- It is recorded once. Loading the new run's page again or calling `advance` on it again changes nothing on the parent run.
|
|
309
|
+
- A job is queued with the app's Active Job adapter, on the `default` queue, that calls `advance` on the parent run. The parent run also moves on the next time its own page is loaded, whichever comes first, so a parent run whose job never runs still moves on when its visitor reloads.
|
|
310
|
+
- The parent run follows the connection leaving the Flow step labelled with that output. An output that matches no connection's label ends the parent run at the Flow step.
|
|
311
|
+
- 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.
|
|
312
|
+
- 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.
|
|
313
|
+
- 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.
|
|
314
|
+
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:
|
|
315
|
+
- 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.
|
|
316
|
+
- 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.
|
|
317
|
+
- Each later page load of the new run, and each later `advance` on it, queues another job that fails the same way.
|
|
318
|
+
- 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.
|
|
319
|
+
- The new run's own `finished` is still called on its visitor's page load.
|
|
320
|
+
5. What holds when an admin removes a flow:
|
|
321
|
+
- Every run of the removed flow is deleted with it.
|
|
322
|
+
- 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.
|
|
323
|
+
- When such a kept run ends, nothing is recorded anywhere and no job is queued.
|
|
324
|
+
- 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.
|
|
325
|
+
- 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.
|
|
326
|
+
6. Find the runs:
|
|
327
|
+
- `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.
|
|
328
|
+
- `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.
|
|
329
|
+
- `run.parent_run` — the run that started `run`.
|
|
330
|
+
7. 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.
|
|
331
|
+
8. 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`:
|
|
332
|
+
|
|
333
|
+
```ruby
|
|
334
|
+
def finished(answers, run)
|
|
335
|
+
return redirect_to main_app.intake_run_path(run.parent_run) if run&.parent_run
|
|
336
|
+
|
|
337
|
+
super
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
9. 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: ...)`.
|
|
342
|
+
10. 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.
|
|
343
|
+
11. 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.
|
|
344
|
+
12. 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.
|
|
345
|
+
13. 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`. 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.
|
|
346
|
+
|
|
229
347
|
### Read a run and its answers
|
|
230
348
|
|
|
231
349
|
1. Find runs scoped to one host so one host never sees another's: `EasyFlow::Run.joins(:flow).where(easy_flow_definitions: { host: "intake" })`.
|
|
@@ -233,10 +351,13 @@ Use this when an admin should edit a value that belongs to one of the app's own
|
|
|
233
351
|
- `run.recorded` — the answers so far, a hash keyed by step id as symbols. A value is a string, or an array of strings for a checklist or any step that takes several answers, empty when nothing was ticked.
|
|
234
352
|
- `run.flow` — the flow it belongs to.
|
|
235
353
|
- `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.
|
|
354
|
+
- `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.
|
|
355
|
+
- `run.inner_runs` — the runs this run's Flow steps started.
|
|
356
|
+
- `run.waiting_on` — the run started at the step this run is stopped at, or `nil` when no run was started there.
|
|
236
357
|
- `run.pinned_definition` — the flow document of the version the run started on.
|
|
237
358
|
- `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.
|
|
238
359
|
- `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.
|
|
239
|
-
- `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.
|
|
360
|
+
- `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".
|
|
240
361
|
3. To show answers with their question text and answer labels, build a runner from the run's pinned document:
|
|
241
362
|
|
|
242
363
|
```ruby
|
|
@@ -257,14 +378,21 @@ Use this when an admin should edit a value that belongs to one of the app's own
|
|
|
257
378
|
## Conventions
|
|
258
379
|
|
|
259
380
|
- A step type's id comes from its class name or the id passed to `EasyFlow.step`, and it is stored in flow documents, so renaming the class or id breaks every flow that uses it.
|
|
260
|
-
- Never give a step type one of the built-in ids: `start`, `terminal`, `question`, `checklist`, `condition`, `switch`, `compare`.
|
|
381
|
+
- Never give a step type one of the built-in ids: `start`, `terminal`, `question`, `checklist`, `condition`, `switch`, `compare`, `flow_step`.
|
|
261
382
|
- Register class-based step types inside `to_prepare`. A type registered anywhere else is lost on code reload in development.
|
|
262
383
|
- Registering a type does not put it in the palette of a host that names a list of offered step types without the type's id. Which step types a host offers is set through `easy_flow-install`, not here.
|
|
263
384
|
- A type that declares `ends_here` is in every host's palette, and a type that declares `begins_here` is in no host's palette.
|
|
264
385
|
- An `options:` lambda is passed nothing, and one list is offered on every host's canvas, so it cannot narrow the options by admin, account or host.
|
|
265
386
|
- A setting declared with `kept_on:` lives only on the app's record, written when the admin saves the step, so a step's code never finds it in `node.config`.
|
|
266
387
|
- A `kept_on:` lambda is given only the step's settings, so it cannot limit which records an admin reaches by admin, account or host.
|
|
388
|
+
- A `:flow` setting holds a flow's id as a string and is not checked against the flows offered, so code reading it looks the flow up through the host.
|
|
389
|
+
- A `starts_a_flow` type reads the flow and version to run from the settings named `flow` and `version`, unless it declares `chooses_flow` and that block returns a choice.
|
|
390
|
+
- 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.
|
|
391
|
+
- A step of a `chooses_flow` type with no `names_by` is named by the chosen flow's title and version number.
|
|
392
|
+
- 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.
|
|
393
|
+
- 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.
|
|
267
394
|
- A type that routes declares the values its output takes, or the canvas offers no connections to label.
|
|
395
|
+
- A run starts only on a version its own flow has published and not retired or withdrawn, and any other version raises `ActiveRecord::RecordInvalid`.
|
|
268
396
|
- Always read a run against `run.pinned_definition`, never the flow's live version, since a run keeps the version it started on after a new one is published.
|
|
269
397
|
- Always look flows and runs up through a host.
|
|
270
398
|
- In a flow that loops, a later visit's answer is keyed `<id>@<n>`, so code that reads answers never assumes one answer per step id. A step id may not contain `@`, and a flow with one is refused when published.
|
|
@@ -279,5 +407,13 @@ Use this when an admin should edit a value that belongs to one of the app's own
|
|
|
279
407
|
- `run.advance` only moves a stored run, and never finishes it; the visitor's next page load does.
|
|
280
408
|
- A flow's output is free text an admin writes on each End step, so code that acts on it handles `nil` and any value it does not expect.
|
|
281
409
|
- A flow's output is read from a stored run only, so a flow the admin set to save nothing gives none.
|
|
410
|
+
- 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.
|
|
411
|
+
- 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.
|
|
412
|
+
- 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`.
|
|
413
|
+
- A Flow step whose named version was retired or withdrawn after publishing raises `EasyFlow::InnerFlowError` naming the step when a run stops there.
|
|
414
|
+
- 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.
|
|
415
|
+
- Only a stored run starts a run at a Flow step.
|
|
416
|
+
- Removing a flow deletes its runs and keeps the runs their Flow steps started with no `parent_run`, so code that follows a run to the run that started it reads `parent_run` and handles `nil`.
|
|
417
|
+
- A flow cannot be removed while a run of another flow is waiting on one of its runs at a Flow step.
|
|
282
418
|
- A `FlowsController` subclass needs all four named routes for its prefix; a missing one raises when the visitor is linked or redirected to it.
|
|
283
419
|
- The engine installs, mounts and configures hosts, the step types each host offers, layouts, the default drawing and checks through `easy_flow-install`, not here.
|
|
@@ -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, hosts and the step types each offers its admins, runs 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, and branching on an answer or comparing a number, step types the host app registers, 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, hosts and the step types each offers its admins, runs and the published version each starts on, 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, and branching on an answer or comparing a number, 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 that start another flow and work out which flow and version to run from their own settings and are named after that flow, 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
|
---
|
|
@@ -11,24 +11,25 @@ This local explains easy_flow and the words it uses. It makes no changes and giv
|
|
|
11
11
|
|
|
12
12
|
easy_flow is a Rails engine for guided, branching flows. An admin builds a flow on a canvas by placing steps and connecting them, previews it, and publishes it. A visitor then walks the published flow one step at a time, and each answer decides which step comes next.
|
|
13
13
|
|
|
14
|
-
Reach for it when an app needs a questionnaire, an intake form, or any decision path that an admin should be able to change without a deploy. The engine ships a start step, an end step, a question step, a checklist step,
|
|
14
|
+
Reach for it when an app needs a questionnaire, an intake form, or any decision path that an admin should be able to change without a deploy. The engine ships a start step, an end step, a question step, a checklist step, three steps that pick a branch from earlier answers, and a Flow step that names another flow. Anything else a flow needs to ask, do, or wait for is a step type the app declares itself.
|
|
15
15
|
|
|
16
16
|
## Interface
|
|
17
17
|
|
|
18
18
|
easy_flow declares no commands of its own for this local. Its surface is split between the other two:
|
|
19
19
|
|
|
20
20
|
- **easy_flow-install** owns putting the engine into an app: its migrations, mounting it, choosing the controller it inherits from, naming hosts and the step types each one offers, choosing the default step drawing, the flow checks, and the admin pages.
|
|
21
|
-
- **easy_flow-develop** owns building on it: declaring and registering step types, including ones that wait and ones with settings kept on the app's own records, moving a run on once what it waits for has happened, serving a host's flows from the app's own controllers and routes, and reading a visitor's run, its answers and the output its flow ended with.
|
|
21
|
+
- **easy_flow-develop** owns building on it: declaring and registering step types, including ones that wait, ones that start another flow, and ones with settings kept on the app's own records, moving a run on once what it waits for has happened, serving a host's flows from the app's own controllers and routes, and starting a run on a chosen published version, reading a visitor's run, its answers, the inner run it is waiting on and the output its flow ended with.
|
|
22
22
|
|
|
23
23
|
## How to use it
|
|
24
24
|
|
|
25
25
|
- To get easy_flow running in an app, or to change how it is configured, including which step types a host's admins can add, use **easy_flow-install**.
|
|
26
|
-
- To add a step type, give a step type its own rule for refusing an answer, make what a step shows depend on the visitor's run, hold a run until something outside the flow has happened, keep a step's setting on one of the app's own records, put a flow on the app's own pages, act on what a visitor answered, or act on the output a visitor's flow ended with, use **easy_flow-develop**.
|
|
26
|
+
- To add a step type, give a step type its own rule for refusing an answer, make what a step shows depend on the visitor's run, hold a run until something outside the flow has happened, add a step type of the app's own that starts another flow, keep a step's setting on one of the app's own records, put a flow on the app's own pages, start a run on a published version other than the live one, act on what a visitor answered, or act on the output a visitor's flow ended with, use **easy_flow-develop**.
|
|
27
27
|
- To give a flow a different output for each way it can end, no code is needed: an admin fills in the output on each End step on the canvas.
|
|
28
28
|
- To branch a flow on an answer or on a number the visitor gave, no code is needed: an admin places one of the built-in branching steps on the canvas.
|
|
29
29
|
- To make a visitor answer a question or tick something on a checklist before going on, no code is needed: an admin marks that step as required on the canvas.
|
|
30
30
|
- To let a visitor tick several answers on one step, no code is needed: an admin places a checklist step on the canvas.
|
|
31
31
|
- To explain an answer to the visitor, no code is needed: an admin fills in that answer's info on a question or checklist step.
|
|
32
|
+
- To start another flow from inside a flow, no code is needed: an admin places a Flow step on the canvas and picks that flow and its version. The connections leaving the step are labelled with the ways that flow can end, and a stored run moves on along the one named after the output its inner run ended with. Clicking the step's card opens the canvas of the flow it runs, and that canvas has a button back to the flow it was opened from. A Flow step cannot start the flow it sits in, directly or through other flows.
|
|
32
33
|
- To ask a step again, such as repeating a question until the visitor says they are done, no code is needed: an admin connects a later step back to an earlier one on the canvas.
|
|
33
34
|
- If the question is only what a word below means, this page is the answer.
|
|
34
35
|
|
|
@@ -36,13 +37,15 @@ easy_flow declares no commands of its own for this local. Its surface is split b
|
|
|
36
37
|
|
|
37
38
|
- **Flow** — one guided path, found by its slug within its host. It holds a working document the admin edits and a list of versions.
|
|
38
39
|
- **Document** — the flow's content: its steps (nodes), the connections between them (edges), a headline and a slug.
|
|
39
|
-
- **Version** — a numbered snapshot of the document. A version is a draft until it is published, and a flow has at most one live version at a time.
|
|
40
|
+
- **Version** — a numbered snapshot of the document. A version is a draft until it is published, and a flow has at most one live version at a time. Publishing another version makes the one that was live superseded. A version can also be retired or withdrawn, and a retired or withdrawn version cannot be published again.
|
|
41
|
+
- **Published version** — a version that is live or superseded, which is any version that has been published and not taken out of service.
|
|
42
|
+
- **Removing a flow** — an admin deleting a flow from the admin's list of flows. It deletes the flow's versions and its runs with it. A run of another flow that one of those runs had started is kept, and no longer records a run that started it. A flow is not removed while a run of it is the inner run that a run of another flow is stopped at a Flow step waiting on. The admin is then told "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".
|
|
40
43
|
- **Canvas** — the admin screen where steps are added, configured, moved, removed and connected, with undo and redo. Publishing happens there.
|
|
41
44
|
- **Preview** — an admin walking the current flow without starting a stored run.
|
|
42
45
|
- **Step type** — what a kind of step is: its display name, its settings (the fields an admin fills in), its outputs (the values it records), and whether it waits for the visitor, acts on its own, or waits for something outside the flow. A step that acts on its own may record a value worked out from the answers so far, pick which connection to follow, or both.
|
|
43
46
|
- **Display** — what a step type hands the visitor's page to draw a step, such as its question text and its answers. A step type may build its display from the step alone, or from the step and the visitor's stored run, so the same step can show something different to each visitor. There is no stored run in an admin's preview or in a flow that carries its answers in the page, and the display is then built without one.
|
|
44
47
|
- **Answer check** — a rule a step type may carry that looks at a visitor's answer and, when the answer is not acceptable, returns the message the visitor sees. A step type with no such rule accepts every answer.
|
|
45
|
-
- **Setting** — one field an admin fills in when configuring a step on the canvas. It is text, a whole number, a decimal, a yes or no, a pick from a list, several picks from a list, an earlier step, an earlier step's output, or a list of entries that each hold their own fields. A setting can be required, limited to a number of picks, or checked by a rule the step type supplies.
|
|
48
|
+
- **Setting** — one field an admin fills in when configuring a step on the canvas. It is text, a whole number, a decimal, a yes or no, a pick from a list, several picks from a list, an earlier step, an earlier step's output, another flow of the same host, or a list of entries that each hold their own fields. A setting can be required, limited to a number of picks, or checked by a rule the step type supplies.
|
|
46
49
|
- **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.
|
|
47
50
|
- **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.
|
|
48
51
|
- **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.
|
|
@@ -58,12 +61,18 @@ easy_flow declares no commands of its own for this local. Its surface is split b
|
|
|
58
61
|
- **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.
|
|
59
62
|
- **Switch** — a built-in branching step that follows the connection labelled with an earlier step's answer.
|
|
60
63
|
- **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.
|
|
64
|
+
- **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.
|
|
65
|
+
- **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.
|
|
66
|
+
- **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.
|
|
67
|
+
- **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.
|
|
68
|
+
- **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.
|
|
69
|
+
- **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.
|
|
61
70
|
- **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`.
|
|
62
71
|
- **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.
|
|
63
72
|
- **Host** — a named part of the app that owns a set of flows. A host sets the visitor layout and the admin layout, how admins are authenticated, how visitors are authorized, how a refusal is answered, and optionally which step types it offers. Flows are always looked up through a host, so one host never sees another's flows.
|
|
64
73
|
- **Offered step types** — the list of step types a host names for its admins to add. A host that names no list offers every registered step type. The list only narrows the palette.
|
|
65
74
|
- **Palette** — the step types an admin can add on a host's canvas. It never holds the start step, always holds the end step, and otherwise holds the step types the host offers. A step already in a flow whose type the host does not offer is still drawn and run.
|
|
66
|
-
- **Run** — one visitor's pass through a flow. A run
|
|
75
|
+
- **Run** — one visitor's pass through a flow. A run may belong to one of the app's own records, its owner, which the app sets. A run is pinned to the version it started on, so publishing a new version does not change a run already under way. A run starts on the live version unless the app chooses another of the flow's published versions. A run is refused when the chosen version is a draft, is retired or withdrawn, or belongs to another flow. It records each answer by visit, and going back discards the last answer on the path.
|
|
67
76
|
- **Stored run** and **carried answers** — the two ways a visitor's progress is held. A stored run is saved by the app and found again by its id. A flow that keeps nothing carries the answers given so far in the page itself and sends them along with each step.
|
|
68
77
|
- **Answers** — the recorded state of a run, keyed by visit. The next step is always worked out from these answers and the connections in the pinned version.
|
|
69
78
|
- **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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: easy_flow-install
|
|
3
|
-
description: Use to hook easy_flow into a project — copying and running its migrations, mounting the engine for each host, setting the controller it inherits from, declaring hosts with their layouts, access methods, the record that owns their flows and the step types each offers, choosing the default step drawing, turning on optional checks, and reaching the admin pages.
|
|
3
|
+
description: Use to hook easy_flow into a project — copying and running its migrations, mounting the engine for each host, setting the controller it inherits from, declaring hosts with their layouts, access methods, the record that owns their flows and the step types each offers, choosing the default step drawing, turning on optional checks, giving the app a job queue that moves a run on once the flow its Flow step, or its own step type that starts a flow, started ends, and reaching the admin pages.
|
|
4
4
|
tools: Bash, Read, Edit
|
|
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
|
---
|
|
@@ -26,7 +26,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
26
26
|
1. Confirm the app has Keystone UI installed (`keystone_ui` in the Gemfile). easy_flow's controllers use its helpers and it is not pulled in by easy_flow. If it is missing, stop and hand off to the `keystone_ui-install` local before continuing.
|
|
27
27
|
2. Read the `keystone_ui` version in the app's `Gemfile.lock`. easy_flow's visitor pages pass each answer's info text to Keystone UI's radio cards and checkbox rows, and easy_flow is built against `keystone_ui` 0.27.0. If the app's version is older, ask the developer whether to run `bundle update keystone_ui` before continuing.
|
|
28
28
|
3. Add `gem "easy_flow"` to the host's `Gemfile` and run `bundle install`.
|
|
29
|
-
4. Run `bin/rails easy_flow:install:migrations`, then `bin/rails db:migrate`. This writes
|
|
29
|
+
4. Run `bin/rails easy_flow:install:migrations`, then `bin/rails db:migrate`. This writes six migrations into `db/migrate` and updates `db/schema.rb`.
|
|
30
30
|
5. Ask the developer which hosts the app needs. A host is one part of the app that owns its own set of flows, and one host never sees another's flows. Ask for each host's name and the path it is served under.
|
|
31
31
|
6. Ask the developer which controller the engine should inherit from. Choosing `"ApplicationController"` gives the engine the app's own authentication methods and helpers. Create `config/initializers/easy_flow.rb` and set it on the first line:
|
|
32
32
|
|
|
@@ -44,17 +44,17 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
44
44
|
host.visitor_authorization_method = :easy_flow_visitor_permitted?
|
|
45
45
|
host.refusal_method = :refuse_flow
|
|
46
46
|
host.owner_method = :current_account
|
|
47
|
-
host.offers = %i[question checklist condition switch compare]
|
|
47
|
+
host.offers = %i[question checklist condition switch compare flow_step]
|
|
48
48
|
end
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
- `layout` — the visitor layout. Defaults to the engine's bare layout, which loads no stylesheet, so a styled app almost always sets it.
|
|
52
|
-
- `admin_layout` — the admin layout. Defaults to `"application"`. It must call `yield :head` inside `<head>`, or the canvas scripts do not load.
|
|
52
|
+
- `admin_layout` — the admin layout. Defaults to `"application"`. It must call `yield :head` inside `<head>`, or the canvas scripts do not load. It must also show `flash[:notice]` and `flash[:alert]`, since the flow list does not show them itself and removing a flow reports its result only there.
|
|
53
53
|
- `admin_authentication_method` — a method on the base controller, called with no arguments before every admin page. With none set, the admin pages are open to anyone.
|
|
54
54
|
- `visitor_authorization_method` — a method on the base controller, called with the flow, returning true when the visitor may run it. With none set, every visitor is refused.
|
|
55
55
|
- `refusal_method` — a method on the base controller, called with the refusal error when a visitor is refused or a flow is unpublished or withdrawn. With none set, the response is `404 Not Found`.
|
|
56
56
|
- `owner_method` — a method on the base controller, called with no arguments, returning the record whose flows the current request works with, such as the signed-in account. When set, the admin pages list, create and edit only that record's flows, a visitor runs only that record's flows, and two records may each hold a flow with the same slug. With none set, every flow in the host is shared by everyone who reaches it. Ask the developer whether each host's flows belong to one record or are shared.
|
|
57
|
-
- `offers` — the step types this host's admins can add from the canvas palette, as a list of step type names. With none set, every registered step type is offered. The engine's own names are `question`, `checklist`, `condition`, `switch` and `
|
|
57
|
+
- `offers` — the step types this host's admins can add from the canvas palette, as a list of step type names. With none set, every registered step type is offered. The engine's own names are `question`, `checklist`, `condition`, `switch`, `compare` and `flow_step`, the last being the Flow step, which names one of the same host's flows. A step type the app declares is named by the id it was declared with, or after its class when it is a step class, so `Steps::Notify` is `notify`. The End step is offered whether it is listed or not, and the Start step is never offered. Ask the developer which step types each host should offer.
|
|
58
58
|
|
|
59
59
|
Each method named here must exist on the base controller. Ask the developer to point at it or write it. Do not invent its logic.
|
|
60
60
|
8. Mount the engine in `config/routes.rb`, once per host. The host name in `defaults` must match a name declared in step 7. When there is more than one mount, give each an `as:` name:
|
|
@@ -77,7 +77,8 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
77
77
|
- It replaces the engine's drawing of every step whose type names no partial, so it draws each answer's hint and info itself or they are not shown.
|
|
78
78
|
- It is not used for a checklist step. A checklist is always drawn by the engine as one checkbox per answer, each with an info button when the answer has info text, and the visitor may tick several.
|
|
79
79
|
10. Leave `EasyFlow.check` out unless the developer asks for it. The engine already turns on `:unrouted_value`, `:unfollowed_path` and `:dead_end` at boot. Any other name raises `EasyFlow::UnknownCheck` when the app boots.
|
|
80
|
-
11.
|
|
80
|
+
11. Check that the app loads Active Job (`require "rails/all"` or `require "active_job/railtie"` in `config/application.rb`) and has a queue adapter that runs jobs from the `default` queue. When a run started by a Flow step, or by a step type of the app's own that starts another flow, ends, the engine records its output in the run it was started from and queues a job on `default` that moves that run on. If no adapter runs that queue, the run that holds the step stays at it. If `config.active_job.queue_adapter` is not set for an environment, ask the developer which adapter to use there.
|
|
81
|
+
12. Start the server and open `<mount path>/manage/flows` for each host.
|
|
81
82
|
|
|
82
83
|
## Conventions
|
|
83
84
|
|
|
@@ -88,6 +89,19 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
|
|
|
88
89
|
- After publishing a flow with a checklist, check that a visitor can tick several answers and go on, and that a checklist marked required refuses to go on with nothing ticked.
|
|
89
90
|
- After upgrading easy_flow, run `bin/rails easy_flow:install:migrations` again and then `bin/rails db:migrate`. Only migrations the app does not already have are copied.
|
|
90
91
|
- After setting a host's `owner_method`, check that two different owners each see only their own flows on `<mount path>/manage/flows`.
|
|
92
|
+
- After install, check that a Flow step placed on the canvas offers the host's flows by title as its flow setting. With a host's `owner_method` set, it offers only that owner's flows. It needs no setting in the initializer.
|
|
93
|
+
- After choosing a flow on a Flow step, check that its canvas card reads "Runs a flow", that clicking the card opens the chosen flow's canvas under the same mount path, and that its Settings button opens the step's settings instead.
|
|
94
|
+
- After opening a chosen flow's canvas from a Flow step's card, check that its header shows a "Back to" button naming the flow it was opened from, by title or by slug when it has no title, and that the button opens that flow's canvas. A canvas opened from the flow list shows no such button.
|
|
95
|
+
- After install, check that a Flow step with no flow chosen, or whose chosen flow has been deleted, shows "missing flow" in red on its canvas card, and that saving a version or publishing is refused until it is fixed. This check is always on and is not turned on with `EasyFlow.check`.
|
|
96
|
+
- After install, check that a Flow step whose chosen flow exists but whose version is not set, does not exist, or was never published or has been retired or withdrawn shows "unrunnable version" in red on its canvas card, and that saving a version or publishing is refused until it names a live or superseded version. This check is always on and is not turned on with `EasyFlow.check`.
|
|
97
|
+
- After install, check that a Flow step whose chosen flow is the flow it sits in, or whose chosen flow leads back to it through the Flow steps of other flows, shows "circular" in red on its canvas card, and that saving a version or publishing is refused until it is fixed. This check is always on and is not turned on with `EasyFlow.check`.
|
|
98
|
+
- 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.
|
|
99
|
+
- 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 11.
|
|
100
|
+
- 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 11 keeps or reports failed jobs, and that every End step of a flow chosen on a Flow step has an output.
|
|
101
|
+
- 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.
|
|
102
|
+
- 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.
|
|
103
|
+
- 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]`.
|
|
104
|
+
- A step type of the app's own that starts another flow is treated as a Flow step by every check above: the "missing flow", "unrunnable version" and "circular" flags, the refusal to remove a flow a waiting run depends on, and the job on `default`. It runs the flow and version its own settings name, or the ones its declaration works out from its other settings, and needs no setting in the initializer beyond being listed in a host's `offers` when that host sets `offers`.
|
|
91
105
|
- After setting a host's `offers`, check that the canvas palette on that host's `<mount path>/manage/flows` lists only those step types and End.
|
|
92
106
|
- Taking a step type off a host's `offers` removes it from the palette only. Steps of that type already in the host's flows stay in them and keep running.
|
|
93
107
|
- The initializer runs once at boot, so a change to it needs a server restart.
|
data/the_local/interface.yml
CHANGED
|
@@ -37,6 +37,8 @@ sources:
|
|
|
37
37
|
- app/models/easy_flow/steps/checklist.rb
|
|
38
38
|
- app/models/easy_flow/step_type.rb
|
|
39
39
|
- app/models/easy_flow/choice.rb
|
|
40
|
+
- app/jobs/easy_flow/advance_parent_job.rb
|
|
41
|
+
- app/models/easy_flow/progress/kept.rb
|
|
40
42
|
- app/models/easy_flow/run.rb
|
|
41
43
|
- app/models/easy_flow/runner.rb
|
|
42
44
|
- app/models/easy_flow/digest.rb
|
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.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tylercschneider
|
|
@@ -84,12 +84,14 @@ files:
|
|
|
84
84
|
- app/javascript/easy_flow/canvas/styles.js
|
|
85
85
|
- app/javascript/easy_flow/canvas/useConnectors.js
|
|
86
86
|
- app/javascript/easy_flow/canvas/useFlow.js
|
|
87
|
+
- app/jobs/easy_flow/advance_parent_job.rb
|
|
87
88
|
- app/models/easy_flow/admission.rb
|
|
88
89
|
- app/models/easy_flow/application_record.rb
|
|
89
90
|
- app/models/easy_flow/asked.rb
|
|
90
91
|
- app/models/easy_flow/canvas.rb
|
|
91
92
|
- app/models/easy_flow/change.rb
|
|
92
93
|
- app/models/easy_flow/choice.rb
|
|
94
|
+
- app/models/easy_flow/chosen_flow.rb
|
|
93
95
|
- app/models/easy_flow/compare.rb
|
|
94
96
|
- app/models/easy_flow/condition.rb
|
|
95
97
|
- app/models/easy_flow/definition.rb
|
|
@@ -98,6 +100,7 @@ files:
|
|
|
98
100
|
- app/models/easy_flow/drawing.rb
|
|
99
101
|
- app/models/easy_flow/edge.rb
|
|
100
102
|
- app/models/easy_flow/edit_history.rb
|
|
103
|
+
- app/models/easy_flow/flow_step.rb
|
|
101
104
|
- app/models/easy_flow/layout.rb
|
|
102
105
|
- app/models/easy_flow/name.rb
|
|
103
106
|
- app/models/easy_flow/node.rb
|
|
@@ -139,6 +142,7 @@ files:
|
|
|
139
142
|
- db/migrate/20260924120200_create_easy_flow_runs.rb
|
|
140
143
|
- db/migrate/20260924130000_add_host_to_easy_flow_definitions.rb
|
|
141
144
|
- db/migrate/20261001120000_add_owner_to_easy_flow_definitions.rb
|
|
145
|
+
- db/migrate/20261007120000_add_parent_run_to_easy_flow_runs.rb
|
|
142
146
|
- lib/easy_flow.rb
|
|
143
147
|
- lib/easy_flow/engine.rb
|
|
144
148
|
- lib/easy_flow/host.rb
|