alembic 0.1.0 → 0.3.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -22
  3. data/app/assets/builds/alembic/page_builder.css +1 -1
  4. data/app/assets/builds/alembic/page_builder.js +3 -3
  5. data/app/controllers/alembic/application_controller.rb +1 -16
  6. data/app/controllers/alembic/flows_controller.rb +6 -90
  7. data/app/controllers/alembic/manage/base_controller.rb +2 -0
  8. data/app/controllers/alembic/manage/flows_controller.rb +12 -44
  9. data/app/controllers/alembic/manage/pages_controller.rb +8 -1
  10. data/app/controllers/alembic/manage/previews_controller.rb +4 -19
  11. data/app/controllers/alembic/pages_controller.rb +19 -0
  12. data/app/controllers/concerns/alembic/summarizes.rb +59 -0
  13. data/app/helpers/alembic/application_helper.rb +2 -2
  14. data/app/helpers/alembic/pages_helper.rb +40 -0
  15. data/app/helpers/alembic/result_blocks_helper.rb +45 -0
  16. data/app/models/alembic/flow/definition_summary.rb +10 -0
  17. data/app/models/alembic/flow/run_summary.rb +10 -0
  18. data/app/models/alembic/flow/summaries.rb +44 -5
  19. data/app/models/alembic/flow/summary_version.rb +1 -1
  20. data/app/models/alembic/outputs/band.rb +1 -1
  21. data/app/models/alembic/outputs/grouped.rb +1 -1
  22. data/app/models/alembic/outputs/lowest.rb +5 -3
  23. data/app/models/alembic/outputs/percentage.rb +1 -1
  24. data/app/models/alembic/outputs/weighted_sum.rb +1 -1
  25. data/app/models/alembic/page/drawing.rb +17 -0
  26. data/app/models/alembic/page/options.rb +53 -0
  27. data/app/models/alembic/page/version.rb +13 -0
  28. data/app/models/alembic/page.rb +32 -0
  29. data/app/models/alembic/result_blocks.rb +29 -0
  30. data/app/models/alembic/summary/report.rb +1 -1
  31. data/app/models/alembic/summary/result.rb +1 -1
  32. data/app/views/alembic/blocks/_answers.html.erb +11 -0
  33. data/app/views/alembic/blocks/_band.html.erb +6 -0
  34. data/app/views/alembic/blocks/_categories.html.erb +11 -0
  35. data/app/views/alembic/blocks/_lead.html.erb +15 -0
  36. data/app/views/alembic/blocks/_score.html.erb +7 -0
  37. data/app/views/alembic/blocks/_weakest.html.erb +12 -0
  38. data/app/views/alembic/flows/complete.html.erb +12 -6
  39. data/app/views/alembic/flows/show.html.erb +6 -6
  40. data/app/views/alembic/flows/summary_page.html.erb +5 -0
  41. data/app/views/alembic/manage/flows/edit.html.erb +16 -10
  42. data/app/views/alembic/manage/pages/_block_content.html.erb +1 -1
  43. data/app/views/alembic/manage/pages/index.html.erb +4 -1
  44. data/app/views/alembic/manage/pages/show.html.erb +7 -1
  45. data/app/views/alembic/pages/show.html.erb +5 -0
  46. data/config/routes.rb +8 -19
  47. data/db/migrate/20260825224538_remove_published_version_from_alembic_diagnostics.rb +21 -1
  48. data/db/migrate/20260922120000_add_slug_to_alembic_pages.rb +6 -0
  49. data/db/migrate/20260922130000_create_alembic_page_versions.rb +16 -0
  50. data/db/migrate/20260924220000_move_flows_onto_easy_flow.rb +166 -0
  51. data/db/migrate/20260930120000_add_summary_page_to_alembic_flow_definition_summaries.rb +6 -0
  52. data/lib/alembic/engine.rb +6 -11
  53. data/lib/alembic/version.rb +1 -1
  54. data/lib/alembic.rb +34 -6
  55. metadata +45 -71
  56. data/app/assets/builds/alembic/canvas.js +0 -1
  57. data/app/controllers/alembic/manage/canvas_controller.rb +0 -183
  58. data/app/controllers/alembic/manage/definitions_controller.rb +0 -15
  59. data/app/controllers/alembic/manage/versions_controller.rb +0 -22
  60. data/app/controllers/concerns/alembic/manage/draws_canvas.rb +0 -35
  61. data/app/javascript/alembic/canvas/Canvas.jsx +0 -197
  62. data/app/javascript/alembic/canvas/Connector.jsx +0 -30
  63. data/app/javascript/alembic/canvas/ConnectorLayer.jsx +0 -31
  64. data/app/javascript/alembic/canvas/Control.jsx +0 -42
  65. data/app/javascript/alembic/canvas/Inspector.jsx +0 -44
  66. data/app/javascript/alembic/canvas/Panel.jsx +0 -67
  67. data/app/javascript/alembic/canvas/Placeholder.jsx +0 -24
  68. data/app/javascript/alembic/canvas/Port.jsx +0 -16
  69. data/app/javascript/alembic/canvas/Records.jsx +0 -34
  70. data/app/javascript/alembic/canvas/StepCard.jsx +0 -57
  71. data/app/javascript/alembic/canvas/Toolbar.jsx +0 -19
  72. data/app/javascript/alembic/canvas/TypePicker.jsx +0 -21
  73. data/app/javascript/alembic/canvas/changes.js +0 -5
  74. data/app/javascript/alembic/canvas/choices.js +0 -4
  75. data/app/javascript/alembic/canvas/flow.js +0 -7
  76. data/app/javascript/alembic/canvas/flowSender.js +0 -22
  77. data/app/javascript/alembic/canvas/ids.js +0 -7
  78. data/app/javascript/alembic/canvas/rows.js +0 -2
  79. data/app/javascript/alembic/canvas/styles.js +0 -3
  80. data/app/javascript/alembic/canvas/useConnectors.js +0 -75
  81. data/app/javascript/alembic/canvas/useFlow.js +0 -17
  82. data/app/javascript/alembic/canvas.jsx +0 -13
  83. data/app/models/alembic/asked.rb +0 -3
  84. data/app/models/alembic/choice.rb +0 -7
  85. data/app/models/alembic/flow/admission.rb +0 -19
  86. data/app/models/alembic/flow/canvas.rb +0 -167
  87. data/app/models/alembic/flow/change.rb +0 -18
  88. data/app/models/alembic/flow/condition.rb +0 -24
  89. data/app/models/alembic/flow/definition.rb +0 -132
  90. data/app/models/alembic/flow/digest.rb +0 -116
  91. data/app/models/alembic/flow/document.rb +0 -131
  92. data/app/models/alembic/flow/drawing.rb +0 -15
  93. data/app/models/alembic/flow/edge.rb +0 -9
  94. data/app/models/alembic/flow/edit_history.rb +0 -75
  95. data/app/models/alembic/flow/layout.rb +0 -88
  96. data/app/models/alembic/flow/name.rb +0 -15
  97. data/app/models/alembic/flow/node.rb +0 -5
  98. data/app/models/alembic/flow/output.rb +0 -23
  99. data/app/models/alembic/flow/progress/kept.rb +0 -38
  100. data/app/models/alembic/flow/progress/loose.rb +0 -43
  101. data/app/models/alembic/flow/progress.rb +0 -11
  102. data/app/models/alembic/flow/registry.rb +0 -25
  103. data/app/models/alembic/flow/run.rb +0 -54
  104. data/app/models/alembic/flow/runner.rb +0 -65
  105. data/app/models/alembic/flow/settings.rb +0 -80
  106. data/app/models/alembic/flow/start.rb +0 -13
  107. data/app/models/alembic/flow/step.rb +0 -42
  108. data/app/models/alembic/flow/step_type/declaration.rb +0 -118
  109. data/app/models/alembic/flow/step_type.rb +0 -69
  110. data/app/models/alembic/flow/switch.rb +0 -17
  111. data/app/models/alembic/flow/terminal.rb +0 -13
  112. data/app/models/alembic/flow/validator.rb +0 -171
  113. data/app/models/alembic/flow/version.rb +0 -26
  114. data/app/models/alembic/flow/violation.rb +0 -9
  115. data/app/models/alembic/flow.rb +0 -44
  116. data/app/models/alembic/runner.rb +0 -13
  117. data/app/models/alembic/steps/question.rb +0 -52
  118. data/app/views/alembic/flow/steps/_choosing.html.erb +0 -9
  119. data/app/views/alembic/flows/step.html.erb +0 -27
  120. data/app/views/alembic/manage/definitions/edit.html.erb +0 -13
  121. data/app/views/alembic/manage/flows/index.html.erb +0 -32
  122. data/app/views/alembic/manage/flows/show.html.erb +0 -26
  123. data/app/views/alembic/manage/versions/index.html.erb +0 -41
@@ -1,13 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Start
4
- include Step
5
-
6
- step_name "Start"
7
-
8
- begins_here
9
-
10
- names_by { |_node| "Start" }
11
- end
12
- end
13
- end
@@ -1,42 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Step
4
- extend ActiveSupport::Concern
5
-
6
- WORDS = (StepType::Declaration.public_instance_methods(false) - [ :to_step_type ]).freeze
7
- BEHAVIOURS = %i[route process].freeze
8
-
9
- included do
10
- class_attribute :declarations, default: []
11
- end
12
-
13
- class_methods do
14
- WORDS.each do |word|
15
- define_method(word) do |*arguments, **options, &block|
16
- self.declarations += [ [ word, arguments, options, block ] ]
17
- end
18
- end
19
-
20
- def step_type
21
- spoken = declarations + behaviours
22
- StepType.define(step_type_id) do
23
- spoken.each { |word, arguments, options, block| public_send(word, *arguments, **options, &block) }
24
- end
25
- end
26
-
27
- def behaviours
28
- BEHAVIOURS.select { |word| method_defined?(word) }
29
- .map { |word| [ word, [], {}, ->(node, state) { new.public_send(word, node, state) } ] }
30
- end
31
-
32
- def register(registry = Flow.registry)
33
- registry.register(step_type)
34
- end
35
-
36
- def step_type_id
37
- name.demodulize.underscore.to_sym
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,118 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class StepType
4
- class Declaration
5
- def initialize(id)
6
- @id = id
7
- @step_name = id.to_s
8
- @fields = {}
9
- @labels = {}
10
- @record_fields = {}
11
- @record_labels = {}
12
- @choices = {}
13
- @limits = {}
14
- @checks = {}
15
- @drawn_from = {}
16
- @outputs_of = {}
17
- @declared_outputs = []
18
- @required = []
19
- @awaits_input = false
20
- @ends_here = false
21
- @begins_here = false
22
- end
23
-
24
- def output(name, type: :string, label: nil, values: nil, from: nil)
25
- @declared_outputs += [ Output.new(name: name, type: type, label: label, values: values, from: from) ]
26
- end
27
-
28
- def displays_by(&display)
29
- @display = display
30
- end
31
-
32
- def drawn_by(template)
33
- @drawn_by = template
34
- end
35
-
36
- def process(&behaviour)
37
- @behaviour = behaviour
38
- end
39
-
40
- def route(&routing)
41
- @routing = routing
42
- end
43
-
44
- def step_name(value)
45
- @step_name = value
46
- end
47
-
48
- def awaits_input
49
- @awaits_input = true
50
- end
51
-
52
- def ends_here
53
- @ends_here = true
54
- end
55
-
56
- def begins_here
57
- @begins_here = true
58
- end
59
-
60
- def names_by(field = nil, &naming)
61
- @naming_field = field
62
- @naming = naming
63
- end
64
-
65
- def setting(name, type: nil, from: nil, outputs_of: nil, label: nil, options: nil, limit: nil, check: nil, required: false, &entries)
66
- type ||= :from_step if from
67
- type ||= :from_step if outputs_of
68
- raise UnknownFieldType, "#{type} is not one of #{FIELD_TYPES.join(', ')}" unless FIELD_TYPES.include?(type)
69
-
70
- @drawn_from[name] = from if from
71
- @outputs_of[name] = outputs_of if outputs_of
72
- @required += [ name ] if required
73
-
74
- declare_entries(name, entries) if type == :list
75
- declare_choices(name, type, options)
76
- @limits[name] = limit if limit
77
- @checks[name] = check if check
78
- @labels[name] = label.presence || name.to_s.humanize
79
- @fields[name] = type
80
- end
81
-
82
- protected
83
-
84
- attr_reader :fields, :labels
85
-
86
- private
87
-
88
- def declare_choices(name, type, options)
89
- return @choices[name] = options if options.present?
90
- raise UnknownFieldType, "a #{type} must offer options" if %i[select multi_select].include?(type)
91
- end
92
-
93
- def declare_entries(name, entries)
94
- raise UnknownFieldType, "a list must say what an entry holds" if entries.nil?
95
-
96
- declared = Declaration.new(:entry).tap { |entry| entry.instance_eval(&entries) }
97
- @record_fields[name] = declared.fields
98
- @record_labels[name] = declared.labels
99
- end
100
-
101
- public
102
-
103
- def to_step_type
104
- StepType.new(id: @id, step_name: @step_name, settings: settings, awaits_input: @awaits_input,
105
- ends_here: @ends_here, begins_here: @begins_here, behaviour: @behaviour, routing: @routing,
106
- display: @display, drawn_by: @drawn_by, naming_field: @naming_field, naming: @naming,
107
- outputs: @declared_outputs)
108
- end
109
-
110
- def settings
111
- Settings.new(fields: @fields, labels: @labels, record_fields: @record_fields,
112
- record_labels: @record_labels, choices: @choices, limits: @limits, checks: @checks,
113
- required: @required, drawn_from: @drawn_from, outputs_of: @outputs_of)
114
- end
115
- end
116
- end
117
- end
118
- end
@@ -1,69 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class StepType
4
- def self.define(id, &declaration)
5
- Declaration.new(id).tap { |decl| decl.instance_eval(&declaration) }.to_step_type
6
- end
7
-
8
- attr_reader :id, :step_name, :settings, :naming_field, :naming, :outputs, :drawn_by
9
-
10
- def initialize(id:, step_name:, settings:, awaits_input:, behaviour:, routing:,
11
- ends_here: false, begins_here: false, display: nil, drawn_by: nil,
12
- naming_field: nil, naming: nil, outputs: [])
13
- @id = id
14
- @step_name = step_name
15
- @settings = settings
16
- @awaits_input = awaits_input
17
- @ends_here = ends_here
18
- @begins_here = begins_here
19
- @behaviour = behaviour
20
- @routing = routing
21
- @display = display
22
- @drawn_by = drawn_by
23
- @naming_field = naming_field
24
- @naming = naming
25
- @outputs = outputs
26
- end
27
-
28
- def display_of(node)
29
- @display&.call(node)
30
- end
31
-
32
- def process(node, state)
33
- @behaviour&.call(node, state)
34
- end
35
-
36
- def route(node, state)
37
- @routing&.call(node, state)
38
- end
39
-
40
- def name_of(node)
41
- @naming&.call(node).presence || node.config[naming_field.to_s].presence
42
- end
43
-
44
- def acts?
45
- @behaviour.present?
46
- end
47
-
48
- def routes?
49
- @routing.present?
50
- end
51
-
52
- def values_of(name, node)
53
- outputs.find { |output| output.name.to_s == name.to_s }&.values_for(node).to_a
54
- end
55
-
56
- def awaits_input?
57
- @awaits_input
58
- end
59
-
60
- def ends_here?
61
- @ends_here
62
- end
63
-
64
- def begins_here?
65
- @begins_here
66
- end
67
- end
68
- end
69
- end
@@ -1,17 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Switch
4
- include Step
5
-
6
- step_name "Switch"
7
-
8
- setting :step, type: :previous_step
9
-
10
- output :choice, type: :string, label: "Choice", from: :step
11
-
12
- def route(node, state)
13
- state[node.config["step"]]
14
- end
15
- end
16
- end
17
- end
@@ -1,13 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Terminal
4
- include Step
5
-
6
- step_name "End"
7
-
8
- ends_here
9
-
10
- names_by { |_node| "End" }
11
- end
12
- end
13
- end
@@ -1,171 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Validator
4
- OPTIONAL = { unrouted_value: :unrouted_values, unfollowed_path: :unfollowed_paths, dead_end: :dead_ends }.freeze
5
-
6
- def initialize(document, registry: Flow.registry, checks: Flow.checks)
7
- @document = document
8
- @registry = registry
9
- @checks = checks
10
- end
11
-
12
- def violations
13
- structural_violations + unmet_requirements + missing_settings + missing_values + beginnings + asked_for
14
- end
15
-
16
- def structural_violations
17
- malformations + unreachable
18
- end
19
-
20
- def malformations
21
- missing_edge_targets + missing_edge_sources + duplicate_ids + no_beginning
22
- end
23
-
24
- private
25
-
26
- def asked_for
27
- @checks.flat_map { |name| send(OPTIONAL.fetch(name)) }
28
- end
29
-
30
- def missing_edge_targets
31
- @document.edges.reject { |edge| known?(edge.to) }
32
- .map { |edge| Violation.new(node: edge.from, problem: :missing_edge_target, detail: edge.to) }
33
- end
34
-
35
- def missing_edge_sources
36
- @document.edges.reject { |edge| known?(edge.from) }
37
- .map { |edge| Violation.new(node: edge.to, problem: :missing_edge_source, detail: edge.from) }
38
- end
39
-
40
- def duplicate_ids
41
- @document.nodes.map(&:id).tally.select { |_id, count| count > 1 }
42
- .map { |id, _count| Violation.new(node: id, problem: :duplicate_id) }
43
- end
44
-
45
- def no_beginning
46
- return [] if known?(@document.entry)
47
-
48
- [ Violation.new(node: nil, problem: :no_beginning) ]
49
- end
50
-
51
- def unmet_requirements
52
- @document.nodes.flat_map do |node|
53
- requirements_for(node).reject { |required| precedes_every_path?(required, node.id) }
54
- .map { |required| Violation.new(node: node.id, problem: :unmet_requirement, detail: required) }
55
- end
56
- end
57
-
58
- def dead_ends
59
- @document.nodes.filter_map { |node| ending_problem(node) }
60
- end
61
-
62
- def ending_problem(node)
63
- leaving = @document.edges_from(node.id).any?
64
- return Violation.new(node: node.id, problem: :past_the_end) if ends_here?(node) && leaving
65
- return if ends_here?(node) || leaving
66
-
67
- Violation.new(node: node.id, problem: :dead_end)
68
- end
69
-
70
- def ends_here?(node)
71
- @registry.registered?(node.type) && @registry.fetch(node.type).ends_here?
72
- end
73
-
74
- def unfollowed_paths
75
- @document.nodes.select { |node| leads_on?(node) }.flat_map { |node| unfollowed_paths_from(node) }
76
- end
77
-
78
- def leads_on?(node)
79
- @registry.registered?(node.type) && !@registry.fetch(node.type).routes?
80
- end
81
-
82
- def unfollowed_paths_from(node)
83
- @document.edges_from(node.id).drop(1)
84
- .map { |edge| Violation.new(node: node.id, problem: :unfollowed_path, detail: edge.to) }
85
- end
86
-
87
- def unrouted_values
88
- @document.nodes.flat_map { |node| unrouted_values_in(node) }
89
- end
90
-
91
- def unrouted_values_in(node)
92
- wired = @document.edges_from(node.id).map { |edge| edge.on.to_s }
93
-
94
- digest.routing_values(node.id).reject { |value| wired.include?(value) }
95
- .map { |value| Violation.new(node: node.id, problem: :unrouted_value, detail: value) }
96
- end
97
-
98
- def digest
99
- Digest.new(@document, registry: @registry)
100
- end
101
-
102
- def beginnings
103
- @document.beginnings.drop(1).map { |id| Violation.new(node: id, problem: :many_beginnings) } + led_into
104
- end
105
-
106
- def led_into
107
- @document.beginnings.select { |id| @document.edges.any? { |edge| edge.to == id } }
108
- .map { |id| Violation.new(node: id, problem: :before_the_beginning) }
109
- end
110
-
111
- def missing_values
112
- @document.nodes.flat_map { |node| missing_values_in(node) }
113
- end
114
-
115
- def missing_values_in(node)
116
- drawn_of(node).filter_map do |name, source|
117
- chosen = node.config[name.to_s]
118
- next if chosen.blank? || node.config[source.to_s].blank?
119
- next if offered_to(node, source).include?(chosen.to_s)
120
-
121
- Violation.new(node: node.id, problem: :missing_value, detail: chosen.to_s)
122
- end
123
- end
124
-
125
- def offered_to(node, source)
126
- naming = @registry.fetch(node.type).settings.outputs_of[source]
127
-
128
- digest.values_of(node.config[naming.to_s], node.config[source.to_s]).map { |value| value["value"].to_s }
129
- end
130
-
131
- def drawn_of(node)
132
- return {} unless @registry.registered?(node.type)
133
-
134
- @registry.fetch(node.type).settings.drawn_from
135
- end
136
-
137
- def missing_settings
138
- @document.nodes.flat_map do |node|
139
- required_of(node).reject { |name| node.config[name.to_s].present? }
140
- .map { |name| Violation.new(node: node.id, problem: :missing_setting, detail: name.to_s) }
141
- end
142
- end
143
-
144
- def required_of(node)
145
- return [] unless @registry.registered?(node.type)
146
-
147
- @registry.fetch(node.type).settings.required
148
- end
149
-
150
- def requirements_for(node)
151
- return [] unless @registry.registered?(node.type)
152
-
153
- @registry.fetch(node.type).settings.requirements_for(node.config)
154
- end
155
-
156
- def precedes_every_path?(required, id)
157
- known?(required) && !@document.reachable(without: required).include?(id)
158
- end
159
-
160
- def unreachable
161
- return [] unless known?(@document.entry)
162
-
163
- (@document.nodes.map(&:id).uniq - @document.reachable).map { |id| Violation.new(node: id, problem: :unreachable) }
164
- end
165
-
166
- def known?(id)
167
- @document.node(id).present?
168
- end
169
- end
170
- end
171
- end
@@ -1,26 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Version < ApplicationRecord
4
- self.table_name = "alembic_flow_versions"
5
-
6
- enum :status, { draft: "draft", live: "live", superseded: "superseded",
7
- retired: "retired", withdrawn: "withdrawn" }
8
-
9
- belongs_to :flow, class_name: "Alembic::Flow::Definition"
10
-
11
- validates :number, uniqueness: { scope: :flow_id }
12
-
13
- def out_of_service?
14
- retired? || withdrawn?
15
- end
16
-
17
- def changes
18
- changes_captured.to_a
19
- end
20
-
21
- FROZEN = %w[definition number flow_id changes_captured].freeze
22
-
23
- before_update { raise ActiveRecord::ReadOnlyRecord if changed.intersect?(FROZEN) }
24
- end
25
- end
26
- end
@@ -1,9 +0,0 @@
1
- module Alembic
2
- module Flow
3
- Violation = Data.define(:node, :problem, :detail) do
4
- def initialize(node:, problem:, detail: nil)
5
- super
6
- end
7
- end
8
- end
9
- end
@@ -1,44 +0,0 @@
1
- module Alembic
2
- module Flow
3
- FIELD_TYPES = %i[string integer float boolean select multi_select previous_step from_step list].freeze
4
-
5
- OUTPUT_TYPES = %i[string integer float boolean].freeze
6
- OPTIONAL_CHECKS = %i[unrouted_value unfollowed_path dead_end].freeze
7
-
8
- class UnknownFieldType < ArgumentError; end
9
- class UnknownOutputType < ArgumentError; end
10
- class UnknownStepType < KeyError; end
11
- class UnknownCheck < ArgumentError; end
12
- class InvalidEdit < StandardError; end
13
-
14
- DRAWING = "alembic/flow/steps/choosing".freeze
15
-
16
- class << self
17
- def draws_with(template)
18
- @drawing = template
19
- end
20
-
21
- def drawing
22
- @drawing.presence || DRAWING
23
- end
24
-
25
- def step(id, &declaration)
26
- registry.register(StepType.define(id, &declaration))
27
- end
28
-
29
- def registry
30
- @registry ||= Registry.new
31
- end
32
-
33
- def check(name)
34
- raise UnknownCheck, "#{name} is not one of #{OPTIONAL_CHECKS.join(', ')}" unless OPTIONAL_CHECKS.include?(name)
35
-
36
- checks << name unless checks.include?(name)
37
- end
38
-
39
- def checks
40
- @checks ||= []
41
- end
42
- end
43
- end
44
- end
@@ -1,13 +0,0 @@
1
- module Alembic
2
- class Runner < Flow::Runner
3
- def question_text(id)
4
- Steps::Question.asked(step(id)&.config)
5
- end
6
-
7
- def choice_label(id, value)
8
- chosen = Steps::Question.choices_in(step(id)).find { |choice| choice.value == value }
9
-
10
- chosen&.label.presence || value
11
- end
12
- end
13
- end
@@ -1,52 +0,0 @@
1
- module Alembic
2
- module Steps
3
- class Question
4
- include Flow::Step
5
-
6
- step_name "Question"
7
-
8
- setting :question, type: :string
9
- setting :category, type: :string
10
- setting :answers, type: :list, required: true do
11
- setting :value, type: :string
12
- setting :label, type: :string
13
- setting :weight, type: :integer
14
- end
15
-
16
- output :answer, type: :string, label: "Answer", values: ->(node) { Question.offered(node.config) }
17
-
18
- names_by :question
19
- awaits_input
20
-
21
- displays_by { |node| Asked.new(id: node.id.to_sym, text: asked(node.config), choices: choices_in(node)) }
22
-
23
- def self.asked(step)
24
- step.to_h["question"] || step.to_h["text"]
25
- end
26
-
27
- def self.answers_of(step)
28
- Array(step.to_h["answers"] || step.to_h["options"])
29
- end
30
-
31
- def self.offered(step)
32
- answers_of(step).map do |answer|
33
- { "value" => answer["value"], "label" => answer["label"].presence || answer["value"] }
34
- end
35
- end
36
-
37
- def self.choices_in(node)
38
- answers_of(node.config).map { |answer| choice_from(answer) }
39
- end
40
-
41
- def self.choice_from(answer)
42
- return Choice.new(value: answer) unless answer.is_a?(Hash)
43
-
44
- Choice.new(value: answer["value"], label: answer["label"], hint: answer["hint"])
45
- end
46
-
47
- def self.category_of(step)
48
- step.to_h["category"] || step.to_h["tag"]
49
- end
50
- end
51
- end
52
- end
@@ -1,9 +0,0 @@
1
- <fieldset>
2
- <legend class="mb-5 text-xl font-semibold leading-snug text-gray-900"><%= step.text %></legend>
3
-
4
- <div class="space-y-3">
5
- <% step.choices.each do |choice| %>
6
- <%= ui_radio_card(name: "answers[#{step.id}]", value: choice.value, label: choice.label.presence || choice.value, hint: choice.hint) %>
7
- <% end %>
8
- </div>
9
- </fieldset>
@@ -1,27 +0,0 @@
1
- <% content_for(:title, @guide.headline) %>
2
-
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" %>
5
-
6
- <div class="mt-6 mb-6">
7
- <%= ui_progress(value: @answers.size, max: [ @guide.steps_on_path(@answers).size, 1 ].max, label: "Question #{@answers.size + 1}") %>
8
- </div>
9
-
10
- <%= form_with url: step_form[:url], method: step_form[:method], data: { turbo: false } do %>
11
- <% if carries_answers? %>
12
- <% @answers.each do |key, value| %>
13
- <%= hidden_field_tag "answers[#{key}]", value %>
14
- <% end %>
15
- <% end %>
16
-
17
- <%= render @drawing, step: @question %>
18
-
19
- <div class="mt-8 flex items-center gap-4">
20
- <%= ui_button(label: "Next", type: :submit) %>
21
-
22
- <% if @answers.any? %>
23
- <button type="submit" name="back" value="1" formnovalidate class="cursor-pointer text-sm text-gray-500 underline underline-offset-2 hover:text-gray-900">← Back</button>
24
- <% end %>
25
- </div>
26
- <% end %>
27
- <% end %>
@@ -1,13 +0,0 @@
1
- <%= ui_page(max_width: :lg) do %>
2
- <p class="mb-4">
3
- <%= link_to "← Back to the flow", alembic.manage_flow_path(@diagnostic), class: "text-sm text-accent-600 hover:text-accent-700" %>
4
- </p>
5
-
6
- <%= ui_form_page(title: "Definition", back_url: alembic.manage_flow_path(@diagnostic), subtitle: @diagnostic.slug) %>
7
-
8
- <%= ui_form(action: alembic.manage_flow_definition_path(@diagnostic), method: :patch) do %>
9
- <%= ui_textarea(name: "definition", value: JSON.pretty_generate(@diagnostic.document), rows: 30) %>
10
-
11
- <%= ui_button(label: "Save", type: :submit) %>
12
- <% end %>
13
- <% end %>
@@ -1,32 +0,0 @@
1
- <%= ui_page(max_width: :xl) do %>
2
- <%= ui_page_header(title: "Flows", subtitle: "Build and edit your flows") %>
3
-
4
- <%= ui_data_table(
5
- items: @diagnostics,
6
- columns: [ { title: "Title" }, { slug: "Slug" }, { kind: "Kind" }, { status: "Status" } ],
7
- empty_message: "No diagnostics yet."
8
- ) do |table| %>
9
- <% table.link(:title) { |diagnostic| alembic.manage_flow_path(diagnostic) } %>
10
- <% table.actions do |diagnostic| %>
11
- <%= ui_form(action: alembic.manage_flow_path(diagnostic), method: :delete) do %>
12
- <%= ui_button(label: "Remove", type: :submit, variant: :secondary) %>
13
- <% end %>
14
- <% end %>
15
- <% end %>
16
-
17
- <%= ui_section(title: "Create a diagnostic") do %>
18
- <% if @diagnostic&.errors&.any? %>
19
- <%= ui_alert(message: @diagnostic.errors.full_messages.to_sentence, type: :error) %>
20
- <% end %>
21
-
22
- <%= ui_form(action: alembic.manage_flows_path, method: :post) do %>
23
- <label class="block text-sm font-medium mb-1">Slug</label>
24
- <%= ui_input(name: "flow[slug]", placeholder: "e.g. business-scorecard") %>
25
-
26
- <label class="block text-sm font-medium mb-1 mt-4">Kind</label>
27
- <%= ui_select(name: "flow[kind]", options: [ [ "Scored", "scored" ], [ "Guide", "guide" ] ]) %>
28
-
29
- <%= ui_button(label: "Create", type: :submit, variant: :secondary) %>
30
- <% end %>
31
- <% end %>
32
- <% end %>