easy_flow 0.24.0 → 0.26.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: 420a32e8886e881ee02d50a95fef57640e8a2d540d1d73313b2a9fdd66206d6b
4
- data.tar.gz: 380a7ef18cf5f6af36ff32d5573a5ee7e70b680582e147ec30ede62d23635e5b
3
+ metadata.gz: 46efb29b090e2a98dc5bf554d2c1faaeb278197e2b0bb98baf1ad7ed12d97334
4
+ data.tar.gz: eae3a957b760f661fe10a1becf31c989b5c3b06a5e39bc0c72b5ea03ade64e1e
5
5
  SHA512:
6
- metadata.gz: ccb487775af4e1ec1737ac27be7a46a6bb53fd804c14c8ed41b7ba8102d5001fc61311d55830ae5570178df79cbca3223071d255c5b812a2b1f30e1a86e2eb23
7
- data.tar.gz: 01445f50ff3f85db4b67c38e66911bc8960ea9d17741af9c5f144d5b2320e14d649d44c335f35385844706e2aca312f2ef9ffe6308bfd49d0f07bd9d6af6badf
6
+ metadata.gz: 2834c585d06a13a36d809a36fd804d46e03011d19910e2b3a08291f9bbc601d57dfe11abb72f6f315bf9cb03dda03f6bb10b69a275320dbf3aa33ff2d6a7af2b
7
+ data.tar.gz: 188e23abda87fb93eff368f4c2c2cb0a85d80a141003b33654f597a8e82de4081bb0829d83cfb32c2cf66049c6767b2609a19bb2d2501a01354407775b34a807
@@ -1,6 +1,6 @@
1
1
  module EasyFlow
2
2
  class FlowsController < ApplicationController
3
- helper_method :flow_start_path, :flow_step_path, :previewing?, :step_form, :carries_answers?
3
+ helper_method :flow_start_path, :flow_step_path, :previewing?, :step_form, :carries_answers?, :flow_address
4
4
 
5
5
  def show
6
6
  @flow = flow
@@ -60,6 +60,12 @@ module EasyFlow
60
60
  Run.start(flow)
61
61
  end
62
62
 
63
+ def flow_address(slug)
64
+ return slug unless flow_host.finds_flows_across_owners
65
+
66
+ (run&.flow || flow).public_code
67
+ end
68
+
63
69
  def flow_start_path(slug)
64
70
  flow_routes.flow_path(slug)
65
71
  end
@@ -75,7 +81,7 @@ module EasyFlow
75
81
  def step_form
76
82
  return { url: run_location(run), method: :patch } if run
77
83
 
78
- { url: flow_step_path(@guide.slug), method: :get }
84
+ { url: flow_step_path(flow_address(@guide.slug)), method: :get }
79
85
  end
80
86
 
81
87
  def carries_answers?
@@ -109,13 +115,25 @@ module EasyFlow
109
115
  end
110
116
 
111
117
  def admitted_run
112
- found = find_run(Run.where(flow: hosted_flows), params[:id])
118
+ found = find_run(Run.where(flow: visited_flows), params[:id])
113
119
 
114
120
  Admission.of_run(found, permitted: permitted?(found.flow))
115
121
  end
116
122
 
117
123
  def flow
118
- @stored_flow ||= admit(hosted_flows.find_by(slug: params[:slug]))
124
+ @stored_flow ||= admit(visited_flow)
125
+ end
126
+
127
+ def visited_flow
128
+ return hosted_flows.find_by(slug: params[:slug]) unless flow_host.finds_flows_across_owners
129
+
130
+ visited_flows.find_by_public_code!(params[:slug])
131
+ rescue ActiveRecord::RecordNotFound
132
+ nil
133
+ end
134
+
135
+ def visited_flows
136
+ flow_host.finds_flows_across_owners ? flow_host.flows : hosted_flows
119
137
  end
120
138
 
121
139
  def flowing_definition(flow)
@@ -0,0 +1,25 @@
1
+ module EasyFlow
2
+ module Steps
3
+ class FillInTheBlank
4
+ include Step
5
+
6
+ Asked = Data.define(:id, :text)
7
+
8
+ step_name "Fill in the blank"
9
+
10
+ setting :question, type: :string
11
+ setting :category, type: :string
12
+ setting :required, type: :boolean
13
+ setting :accepted_answers, type: :list, required: true do
14
+ setting :value, type: :string
15
+ end
16
+
17
+ names_by :question
18
+ awaits_input
19
+ drawn_by "easy_flow/steps/typing"
20
+ answer_check { |node, value| "Type an answer to go on." if node.config["required"] && value.blank? }
21
+
22
+ displays_by { |node| Asked.new(id: node.id.to_sym, text: node.config["question"]) }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module EasyFlow
2
+ module Steps
3
+ class MultipleSelect
4
+ include Step
5
+
6
+ step_name "Multiple select"
7
+
8
+ setting :question, type: :string
9
+ setting :category, type: :string
10
+ setting :required, type: :boolean
11
+ setting :answers, type: :list, required: true do
12
+ setting :value, type: :string
13
+ setting :label, type: :string
14
+ setting :weight, type: :integer
15
+ end
16
+
17
+ names_by :question
18
+ awaits_input
19
+ drawn_by "easy_flow/steps/ticking"
20
+ answer_check { |node, value| "Pick at least one answer to go on." if node.config["required"] && Array(value).compact_blank.empty? }
21
+
22
+ displays_by { |node| Asked.new(id: node.id.to_sym, text: node.config["question"], choices: Question.choices_in(node)) }
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  <% content_for(:title, "Complete") %>
2
2
 
3
- <% starting_over = flow_start_path(@guide.slug) %>
3
+ <% starting_over = flow_start_path(flow_address(@guide.slug)) %>
4
4
 
5
5
  <%= ui_page(max_width: :md) do %>
6
6
  <%= ui_page_header(title: "That's everything", subtitle: "Thanks — here's what you told us") do |header| %>
@@ -6,11 +6,11 @@
6
6
  <%= ui_section do %>
7
7
  <div class="flex flex-wrap items-center gap-3">
8
8
  <% if @flow.each_step? && @flow.definition.present? && !previewing? %>
9
- <%= ui_form(action: flow_routes.flow_runs_path(@flow.slug), method: :post) do %>
9
+ <%= ui_form(action: flow_routes.flow_runs_path(flow_address(@flow.slug)), method: :post) do %>
10
10
  <%= ui_button(label: @flow.start_label.presence || "Start", type: :submit) %>
11
11
  <% end %>
12
12
  <% else %>
13
- <%= ui_button(label: @flow.start_label.presence || "Start", href: flow_step_path(@flow.slug)) %>
13
+ <%= ui_button(label: @flow.start_label.presence || "Start", href: flow_step_path(flow_address(@flow.slug))) %>
14
14
  <% end %>
15
15
  </div>
16
16
  <% end %>
@@ -1,7 +1,7 @@
1
1
  <% content_for(:title, @guide.headline) %>
2
2
 
3
3
  <%= ui_page(max_width: :md) do %>
4
- <%= link_to "← Start over", flow_start_path(@guide.slug), class: "text-sm text-accent-600 hover:text-accent-700" %>
4
+ <%= link_to "← Start over", flow_start_path(flow_address(@guide.slug)), class: "text-sm text-accent-600 hover:text-accent-700" %>
5
5
 
6
6
  <div class="mt-6 mb-6">
7
7
  <%= ui_progress(value: @answers.size, max: [ @guide.steps_on_path(@answers).size, 1 ].max, label: "Question #{@answers.size + 1}") %>
@@ -14,7 +14,7 @@
14
14
  <% if @waiting %>
15
15
  <p class="ks-hint">Waiting for <%= @waiting %>.</p>
16
16
  <% if carries_answers? %>
17
- <div class="mt-4"><%= ui_button(label: "Check again", href: "#{flow_step_path(@guide.slug)}?#{{ answers: @answers, completed_at: @progress.completed_at.transform_values(&:iso8601) }.to_query}", variant: :secondary) %></div>
17
+ <div class="mt-4"><%= ui_button(label: "Check again", href: "#{flow_step_path(flow_address(@guide.slug))}?#{{ answers: @answers, completed_at: @progress.completed_at.transform_values(&:iso8601) }.to_query}", variant: :secondary) %></div>
18
18
  <% end %>
19
19
  <% else %>
20
20
  <%= form_with url: step_form[:url], method: step_form[:method], multipart: true, data: { turbo: false } do %>
@@ -0,0 +1,4 @@
1
+ <label class="block">
2
+ <span class="ks-section-title mb-5 block text-xl font-semibold leading-snug"><%= step.text %></span>
3
+ <%= ui_input(name: "answers[#{step.id}]") %>
4
+ </label>
@@ -20,6 +20,8 @@ module EasyFlow
20
20
 
21
21
  EasyFlow::Steps::Question.register
22
22
  EasyFlow::Steps::Checklist.register
23
+ EasyFlow::Steps::MultipleSelect.register
24
+ EasyFlow::Steps::FillInTheBlank.register
23
25
  EasyFlow::Steps::FileUpload.register
24
26
  EasyFlow::Condition.register
25
27
  EasyFlow::Switch.register
@@ -2,7 +2,7 @@ module EasyFlow
2
2
  class Host
3
3
  attr_reader :name
4
4
  attr_writer :layout, :admin_layout, :offers, :allowed_answers
5
- attr_accessor :admin_authentication_method, :visitor_authorization_method, :refusal_method, :owner_method
5
+ attr_accessor :admin_authentication_method, :visitor_authorization_method, :refusal_method, :owner_method, :finds_flows_across_owners
6
6
 
7
7
  def initialize(name)
8
8
  @name = name.to_s
@@ -1,3 +1,3 @@
1
1
  module EasyFlow
2
- VERSION = "0.24.0"
2
+ VERSION = "0.26.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, 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.
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 fifteen: 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), Multiple select (`:multiple_select`, a question with a category and a list of answers, each with a value, a label and a weight, of which the visitor ticks any number, recording the list of ticked values, which the admin can mark required so a visitor who ticks nothing is refused with `Pick at least one answer to go on.`), Fill in the blank (`:fill_in_the_blank`, a question with a category and a list of accepted answers, each with a value, which the visitor answers by typing into a text box, recording the text exactly as typed, whose accepted answers are never sent to the visitor's page, which the admin can mark required so a visitor who types nothing or only spaces is refused with `Type an answer to go on.`; easy_flow records the answer and does not mark it right or wrong), 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
 
@@ -36,7 +36,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
36
36
  - It only picks which connection to follow — define `route`.
37
37
  - It holds the visitor until something outside the flow has happened, such as a payment arriving or a reviewer approving — declare `waits_until`. When what it waits for is only a time, ask the developer whether the built-in Wait step is enough first; see "Hold a run until a time".
38
38
  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.
39
- 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`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait` or `flow_step`:
39
+ 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`, `multiple_select`, `fill_in_the_blank`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait` or `flow_step`:
40
40
 
41
41
  ```ruby
42
42
  module FlowSteps
@@ -534,7 +534,7 @@ Use this when a visitor's run should stop until a set time has passed, such as a
534
534
  ## Conventions
535
535
 
536
536
  - 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.
537
- - Never give a step type one of the built-in ids: `start`, `terminal`, `question`, `checklist`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait`, `flow_step`.
537
+ - Never give a step type one of the built-in ids: `start`, `terminal`, `question`, `checklist`, `multiple_select`, `fill_in_the_blank`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait`, `flow_step`.
538
538
  - Register class-based step types inside `to_prepare`. A type registered anywhere else is lost on code reload in development.
539
539
  - 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.
540
540
  - 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.
@@ -11,7 +11,7 @@ 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, a file upload step, five steps that pick a branch from earlier answers, a Count step that counts earlier answers, a Wait step that holds a run until a time, 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.
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, a multiple select step, a fill in the blank step, a file upload step, five steps that pick a branch from earlier answers, a Count step that counts earlier answers, a Wait step that holds a run until a time, 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
 
@@ -60,6 +60,8 @@ easy_flow declares no commands of its own for this local. Its surface is split b
60
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.
61
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.
62
62
  - **Checklist** — the built-in step type that asks the visitor to tick any number of answers: a question text, a yes-or-no required setting, and a list of answers, each with a value, a label and an info. The list of ticked values is recorded, and a checklist left with nothing ticked records an empty list.
63
+ - **Multiple select** — the built-in step type that asks the visitor to tick any number of answers, for a question that has several right answers: a question text, a category, a yes-or-no required setting, and a list of answers, each with a value, a label and a weight. The list of ticked values is recorded, and a required one left with nothing ticked is refused with the message "Pick at least one answer to go on."
64
+ - **Fill in the blank** — the built-in step type that asks the visitor to type an answer: a question text, a category, a yes-or-no required setting, and a list of accepted answers. The text typed is recorded as typed, the accepted answers are never sent to the visitor's page, and a required one left blank is refused with the message "Type an answer to go on." easy_flow does not mark the typed answer right or wrong.
63
65
  - **File upload** — the built-in step type that asks the visitor for one file: a question text, the accepted file types, and a yes-or-no required setting. The accepted file types are a comma-separated list of extensions, such as `.pdf, .png`, and an empty list accepts any file. The visitor sees a styled file upload control labelled with the question text. When accepted file types are set, the visitor's file picker offers only those types, and a line under the control reads "Accepts" followed by the list exactly as the admin wrote it, such as "Accepts .pdf, .png". A file whose extension is not on the list is refused with a message naming the accepted types, such as "Choose a .pdf or .png file.", and is not handed to the file store. A required file upload left empty is refused with "Choose a file to go on." A file can only be uploaded in a flow that keeps a stored run, so a flow that carries its answers in the page refuses one with "A file can only be uploaded in a flow that saves its runs."
64
66
  - **File store** — the object the app sets to keep uploaded files. It takes an accepted file, keeps it, and returns a reference, and that reference is what the run records as the step's answer. When a step's answer is sent as a list holding several files, such as from a step type of the app's own, each file is handed to the store and the run records the list of references. It also gives the name of the file a reference points to, which is what the read-back shows. When a file is accepted and the app has set no file store, the step fails with an error saying a file store must be set.
65
67
  - **Hint** — a short line of text shown under an answer's label on a question step.
@@ -45,7 +45,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
45
45
  host.visitor_authorization_method = :easy_flow_visitor_permitted?
46
46
  host.refusal_method = :refuse_flow
47
47
  host.owner_method = :current_account
48
- host.offers = %i[question checklist file_upload condition switch compare compare_two_values all_or_any count wait flow_step]
48
+ host.offers = %i[question checklist multiple_select fill_in_the_blank file_upload condition switch compare compare_two_values all_or_any count wait flow_step]
49
49
  end
50
50
  ```
51
51
 
@@ -55,7 +55,7 @@ A Rails engine for flows an admin draws on a canvas and a visitor runs one step
55
55
  - `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.
56
56
  - `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`.
57
57
  - `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.
58
- - `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`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait` and `flow_step`, `file_upload` being the File upload step, which needs the file store from step 10, `compare_two_values` being the Compare two values step, which compares the outputs of two earlier steps, `all_or_any` being the All of or Any of step, which decides whether all or any of several such comparisons hold, `count` being the Count step, which counts how often an earlier step was answered, `wait` being the Wait step, which holds a run until a time counted from an earlier answer or until a date, and `flow_step` 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
+ - `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`, `multiple_select`, `fill_in_the_blank`, `file_upload`, `condition`, `switch`, `compare`, `compare_two_values`, `all_or_any`, `count`, `wait` and `flow_step`, `file_upload` being the File upload step, which needs the file store from step 10, `compare_two_values` being the Compare two values step, which compares the outputs of two earlier steps, `all_or_any` being the All of or Any of step, which decides whether all or any of several such comparisons hold, `count` being the Count step, which counts how often an earlier step was answered, `wait` being the Wait step, which holds a run until a time counted from an earlier answer or until a date, and `flow_step` 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.
59
59
 
60
60
  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.
61
61
  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:
@@ -41,6 +41,8 @@ sources:
41
41
  - app/models/easy_flow/terminal.rb
42
42
  - app/models/easy_flow/steps/question.rb
43
43
  - app/models/easy_flow/steps/checklist.rb
44
+ - app/models/easy_flow/steps/multiple_select.rb
45
+ - app/models/easy_flow/steps/fill_in_the_blank.rb
44
46
  - app/models/easy_flow/steps/file_upload.rb
45
47
  - app/models/easy_flow/count.rb
46
48
  - app/models/easy_flow/compare_two_values.rb
@@ -60,6 +62,7 @@ sources:
60
62
  - app/views/easy_flow/flows/complete.html.erb
61
63
  - app/views/easy_flow/steps/_choosing.html.erb
62
64
  - app/views/easy_flow/steps/_ticking.html.erb
65
+ - app/views/easy_flow/steps/_typing.html.erb
63
66
  - app/views/easy_flow/steps/_uploading.html.erb
64
67
  - app/controllers/easy_flow/manage/previews_controller.rb
65
68
  - config/routes.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.24.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tylercschneider
@@ -137,6 +137,8 @@ files:
137
137
  - app/models/easy_flow/step_type/declaration.rb
138
138
  - app/models/easy_flow/steps/checklist.rb
139
139
  - app/models/easy_flow/steps/file_upload.rb
140
+ - app/models/easy_flow/steps/fill_in_the_blank.rb
141
+ - app/models/easy_flow/steps/multiple_select.rb
140
142
  - app/models/easy_flow/steps/question.rb
141
143
  - app/models/easy_flow/switch.rb
142
144
  - app/models/easy_flow/terminal.rb
@@ -155,6 +157,7 @@ files:
155
157
  - app/views/easy_flow/manage/versions/index.html.erb
156
158
  - app/views/easy_flow/steps/_choosing.html.erb
157
159
  - app/views/easy_flow/steps/_ticking.html.erb
160
+ - app/views/easy_flow/steps/_typing.html.erb
158
161
  - app/views/easy_flow/steps/_uploading.html.erb
159
162
  - app/views/layouts/easy_flow/application.html.erb
160
163
  - config/routes.rb