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,131 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Document
4
- def initialize(document, registry: Flow.registry)
5
- @document = document
6
- @registry = registry
7
- end
8
-
9
- def nodes
10
- Array(@document["nodes"]).map { |node| Node.new(id: node["id"], type: node["type"], config: node.except("id", "type")) }
11
- end
12
-
13
- def edges
14
- Array(@document["edges"]).map { |edge| Edge.new(from: edge["from"], to: edge["to"], on: edge["on"]) }
15
- end
16
-
17
- def entry
18
- beginnings.first
19
- end
20
-
21
- def beginnings
22
- nodes.select { |node| begins_here?(node) }.map(&:id)
23
- end
24
-
25
- def begins_here?(node)
26
- @registry.registered?(node.type) && @registry.fetch(node.type).begins_here?
27
- end
28
-
29
- def node(id)
30
- nodes.find { |node| node.id == id }
31
- end
32
-
33
- def edges_from(id)
34
- edges.select { |edge| edge.from == id }
35
- end
36
-
37
- def reachable(without: nil)
38
- found = []
39
- frontier = [ entry ]
40
-
41
- while (id = frontier.shift)
42
- next if found.include?(id) || id == without
43
-
44
- found << id
45
- frontier.concat(edges_from(id).map(&:to))
46
- end
47
-
48
- found
49
- end
50
-
51
- def to_h
52
- @document
53
- end
54
-
55
- def insert(node, on:, leaving: nil)
56
- source, target = on
57
- replaced = raw_edges.find { |edge| edge["from"] == source && edge["to"] == target }
58
- bridged = [ { "from" => source, "to" => node["id"], "on" => replaced&.fetch("on", nil) }.compact,
59
- { "from" => node["id"], "to" => target, "on" => leaving }.compact ]
60
-
61
- with(nodes: raw_nodes + [ node ], edges: (raw_edges - [ replaced ]) + bridged)
62
- end
63
-
64
- def add(node)
65
- with(nodes: raw_nodes + [ node ], edges: raw_edges)
66
- end
67
-
68
- def connect(from:, to:, on: nil)
69
- with(nodes: raw_nodes, edges: raw_edges + [ { "from" => from, "to" => to, "on" => on }.compact ])
70
- end
71
-
72
- def disconnect(from:, to:)
73
- with(nodes: raw_nodes, edges: raw_edges.reject { |edge| edge["from"] == from && edge["to"] == to })
74
- end
75
-
76
- def move(id, on:, leaving: nil)
77
- step = raw_nodes.find { |node| node["id"] == id }
78
- return self unless step
79
-
80
- remove(id).insert(step, on: on, leaving: leaving)
81
- end
82
-
83
- def configure(id, config)
84
- reconfigured = raw_nodes.map do |node|
85
- node["id"] == id ? node.slice("id", "type").merge(config) : node
86
- end
87
-
88
- with(nodes: reconfigured, edges: raw_edges)
89
- end
90
-
91
- def rewire(from:, to:, target:)
92
- rewritten = raw_edges.map do |edge|
93
- edge["from"] == from && edge["to"] == to ? edge.merge("to" => target) : edge
94
- end
95
-
96
- with(nodes: raw_nodes, edges: rewritten)
97
- end
98
-
99
- def remove(id)
100
- incoming = raw_edges.select { |edge| edge["to"] == id }
101
- outgoing = raw_edges.select { |edge| edge["from"] == id }
102
-
103
- with(nodes: raw_nodes.reject { |node| node["id"] == id },
104
- edges: (raw_edges - incoming - outgoing) + bridges(incoming, outgoing))
105
- end
106
-
107
- private
108
-
109
- def bridges(incoming, outgoing)
110
- incoming.product(outgoing).map do |arriving, leaving|
111
- { "from" => arriving["from"], "to" => leaving["to"], "on" => arriving["on"] }.compact
112
- end
113
- end
114
-
115
- def raw_nodes
116
- Array(@document["nodes"])
117
- end
118
-
119
- def raw_edges
120
- Array(@document["edges"])
121
- end
122
-
123
- def with(nodes:, edges:)
124
- Document.new(@document.merge("nodes" => nodes, "edges" => edges), registry: @registry).tap do |edited|
125
- broken = Validator.new(edited, registry: @registry).malformations
126
- raise InvalidEdit, broken.map { |violation| "#{violation.node}: #{violation.problem}" }.join(", ") if broken.any?
127
- end
128
- end
129
- end
130
- end
131
- end
@@ -1,15 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Drawing
4
- def self.of(node, registry = Flow.registry)
5
- named(node, registry).presence || Flow.drawing
6
- end
7
-
8
- def self.named(node, registry)
9
- return unless node && registry.registered?(node.type)
10
-
11
- registry.fetch(node.type).drawn_by
12
- end
13
- end
14
- end
15
- end
@@ -1,9 +0,0 @@
1
- module Alembic
2
- module Flow
3
- Edge = Data.define(:from, :to, :on) do
4
- def initialize(from:, to:, on: nil)
5
- super
6
- end
7
- end
8
- end
9
- end
@@ -1,75 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class EditHistory
4
- def initialize(flow)
5
- @flow = flow
6
- end
7
-
8
- def undoable?
9
- undoable.any?
10
- end
11
-
12
- def redoable?
13
- undone.any?
14
- end
15
-
16
- def undoable
17
- @flow.undo_history.to_a + @flow.changes_since_version.to_a
18
- end
19
-
20
- def undo_change
21
- undone_change = undoable.last
22
- return unless undone_change
23
-
24
- @flow.update!(document: undone_change["before"],
25
- undone_changes: undone + [ undone_change.merge("after" => @flow.document) ],
26
- undo_history: @flow.undo_history.to_a[0...-1],
27
- changes_since_version: @flow.changes_since_version.to_a[0...-1])
28
- end
29
-
30
- def redo_change
31
- redone = undone.last
32
- return unless redone
33
-
34
- @flow.update!(document: redone["after"], undone_changes: undone[0...-1],
35
- changes_since_version: @flow.changes_since_version.to_a + [ redone.except("after") ])
36
- end
37
-
38
- def record(edited, action, steps, before)
39
- @flow.update!(document: edited.to_h, undone_changes: [],
40
- changes_since_version: with(edited, action, steps, before))
41
- end
42
-
43
- def edit_document(payload)
44
- @flow.update!(document: payload, undone_changes: [],
45
- changes_since_version: @flow.changes_since_version.to_a + [ edited_by_hand ])
46
- end
47
-
48
- private
49
-
50
- def with(edited, action, steps, before)
51
- return @flow.changes_since_version.to_a unless action
52
-
53
- @flow.changes_since_version.to_a +
54
- [ { "action" => action.to_s, "steps" => steps.map(&:to_s),
55
- "named" => steps.map { |id| named(edited, id) }, "before" => before } ]
56
- end
57
-
58
- def named(edited, id)
59
- Name.of(edited.node(id.to_s) || document.node(id.to_s))
60
- end
61
-
62
- def document
63
- Document.new(@flow.document || @flow.definition || {})
64
- end
65
-
66
- def undone
67
- @flow.undone_changes.to_a
68
- end
69
-
70
- def edited_by_hand
71
- { "action" => "edited", "steps" => [], "named" => [ "the definition" ], "before" => @flow.document }
72
- end
73
- end
74
- end
75
- end
@@ -1,88 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Layout
4
- SPREAD = 2
5
-
6
- def initialize(document)
7
- @document = document
8
- end
9
-
10
- def positions
11
- rows = depths
12
- columns = columns_for(rows)
13
-
14
- rows.to_h { |id, row| [ id, { "row" => row, "column" => columns[id] } ] }
15
- end
16
-
17
- private
18
-
19
- def depths
20
- walked = walk_from_entry
21
-
22
- walked.merge(stranded(walked).index_with(next_row(walked)))
23
- end
24
-
25
- def walk_from_entry
26
- seen = {}
27
- frontier = starting_points.map { |id| [ id, 0 ] }
28
-
29
- until frontier.empty?
30
- id, depth = frontier.shift
31
- next if seen.key?(id)
32
-
33
- seen[id] = depth
34
- @document.edges_from(id).each { |edge| frontier << [ edge.to, depth + 1 ] if known?(edge.to) }
35
- end
36
-
37
- seen
38
- end
39
-
40
- def starting_points
41
- return [ @document.entry ] if known?(@document.entry)
42
-
43
- arrived = @document.edges.map(&:to)
44
- @document.nodes.map(&:id).reject { |id| arrived.include?(id) }
45
- end
46
-
47
- def columns_for(rows)
48
- @columns = {}
49
- @next_leaf = 0
50
- rows.keys.each { |id| place(id, branches(rows)) }
51
- @columns
52
- end
53
-
54
- def branches(rows)
55
- claimed = {}
56
-
57
- rows.keys.to_h do |id|
58
- mine = @document.edges_from(id).map(&:to).uniq
59
- .select { |child| rows[child] == rows[id] + 1 && !claimed.key?(child) }
60
- mine.each { |child| claimed[child] = true }
61
- [ id, mine ]
62
- end
63
- end
64
-
65
- def place(id, branches)
66
- return @columns[id] if @columns.key?(id)
67
-
68
- mine = branches[id].to_a
69
- return @columns[id] = (@next_leaf += SPREAD) - SPREAD if mine.empty?
70
-
71
- spans = mine.map { |child| place(child, branches) }
72
- @columns[id] = (spans.min + spans.max) / 2
73
- end
74
-
75
- def stranded(walked)
76
- @document.nodes.map(&:id).uniq - walked.keys
77
- end
78
-
79
- def next_row(walked)
80
- (walked.values.max || -1) + 1
81
- end
82
-
83
- def known?(id)
84
- @document.node(id).present?
85
- end
86
- end
87
- end
88
- end
@@ -1,15 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Name
4
- def self.of(node, registry = Flow.registry)
5
- return "" unless node
6
-
7
- named(node, registry) || node.id
8
- end
9
-
10
- def self.named(node, registry)
11
- registry.fetch(node.type).name_of(node) if registry.registered?(node.type)
12
- end
13
- end
14
- end
15
- end
@@ -1,5 +0,0 @@
1
- module Alembic
2
- module Flow
3
- Node = Data.define(:id, :type, :config)
4
- end
5
- end
@@ -1,23 +0,0 @@
1
- module Alembic
2
- module Flow
3
- Output = Data.define(:name, :label, :type, :values, :from) do
4
- def initialize(name:, label: nil, type: :string, values: nil, from: nil)
5
- raise UnknownOutputType, "#{type} is not one of #{OUTPUT_TYPES.join(', ')}" unless OUTPUT_TYPES.include?(type)
6
-
7
- super(name: name, label: label.presence || name.to_s.humanize, type: type, values: values, from: from)
8
- end
9
-
10
- def values_for(node)
11
- Array(values.respond_to?(:call) ? values.call(node) : values).map { |value| taken(value) }
12
- end
13
-
14
- private
15
-
16
- def taken(value)
17
- return value if value.is_a?(Hash)
18
-
19
- { "value" => value, "label" => value.to_s }
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Progress
4
- class Kept
5
- def initialize(run)
6
- @run = run
7
- end
8
-
9
- def definition
10
- @run.pinned_definition
11
- end
12
-
13
- def summary_of(state)
14
- return [] if @run.pinned_summary.blank?
15
-
16
- Summary::Report.new(@run.pinned_summary)
17
- .results(Summary::Run.new(state: state, steps: @run.pinned_steps))
18
- end
19
-
20
- def recorded
21
- @run.recorded
22
- end
23
-
24
- def record(id, value)
25
- @run.record(id.to_sym, value)
26
- end
27
-
28
- def finish(_state)
29
- @run
30
- end
31
-
32
- def discard_last
33
- @run.discard_last
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,43 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Progress
4
- class Loose
5
- def initialize(flow, answers, definition = nil)
6
- @flow = flow
7
- @answers = answers.to_h.symbolize_keys
8
- @definition = definition
9
- end
10
-
11
- def definition
12
- @definition || @flow.live_definition
13
- end
14
-
15
- def summary_of(state)
16
- return [] unless @flow.summaries.any?
17
-
18
- @flow.summaries.of(state)
19
- end
20
-
21
- def recorded
22
- @answers
23
- end
24
-
25
- def record(id, value)
26
- @answers = @answers.merge(id.to_sym => value)
27
- end
28
-
29
- def finish(state)
30
- return unless @flow.on_finish?
31
-
32
- Run.start(@flow).tap { |run| run.update!(recorded: state) }
33
- end
34
-
35
- def discard_last
36
- last = Runner.new(@flow.live_definition).state_on_path(@answers).keys.last
37
-
38
- @answers = @answers.except(last) if last
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,11 +0,0 @@
1
- module Alembic
2
- module Flow
3
- module Progress
4
- def self.for(flow, run: nil, answers: {}, definition: nil)
5
- return Kept.new(run) if run
6
-
7
- Loose.new(flow, answers, definition)
8
- end
9
- end
10
- end
11
- end
@@ -1,25 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Registry
4
- def initialize
5
- @step_types = {}
6
- end
7
-
8
- def register(step_type)
9
- @step_types[step_type.id.to_sym] = step_type
10
- end
11
-
12
- def fetch(id)
13
- @step_types.fetch(id.to_sym) { raise UnknownStepType, "no step type registered as #{id}" }
14
- end
15
-
16
- def registered?(id)
17
- id.present? && @step_types.key?(id.to_sym)
18
- end
19
-
20
- def step_types
21
- @step_types.values
22
- end
23
- end
24
- end
25
- end
@@ -1,54 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Run < ApplicationRecord
4
- self.table_name = "alembic_flow_runs"
5
-
6
- belongs_to :flow, class_name: "Alembic::Flow::Definition"
7
- belongs_to :definition_version, class_name: "Alembic::Flow::Version"
8
- belongs_to :summary_version, class_name: "Alembic::Flow::SummaryVersion", optional: true
9
- belongs_to :owner, polymorphic: true, optional: true
10
-
11
- def self.start(flow)
12
- create!(flow: flow, definition_version: flow.live_version,
13
- summary_version: flow.summaries.current_version)
14
- end
15
-
16
- def record(step_id, value)
17
- update!(recorded: recorded.merge(step_id => value))
18
- end
19
-
20
- def recorded
21
- super.to_h.symbolize_keys
22
- end
23
-
24
- def pinned_definition
25
- definition_version.definition.to_h
26
- end
27
-
28
- def pinned_summary
29
- summary_version&.summary.to_h
30
- end
31
-
32
- def next_step(state)
33
- digest.next_step(state.transform_keys(&:to_s))
34
- end
35
-
36
- def walked(state)
37
- digest.state_on_path(state.transform_keys(&:to_s))
38
- end
39
-
40
- def digest
41
- @digest ||= Digest.new(Document.new(pinned_definition))
42
- end
43
-
44
- def discard_last
45
- last = walked(recorded).keys.map(&:to_sym).last
46
- update!(recorded: recorded.except(last)) if last
47
- end
48
-
49
- def pinned_steps
50
- Array(definition_version.definition.to_h["nodes"]).index_by { |node| node["id"] }
51
- end
52
- end
53
- end
54
- end
@@ -1,65 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Runner
4
- def initialize(document, registry: Flow.registry)
5
- @document = document.to_h
6
- @registry = registry
7
- @digest = Digest.new(Document.new(@document, registry: registry), registry: registry)
8
- end
9
-
10
- def slug
11
- @document["slug"]
12
- end
13
-
14
- def headline
15
- @document["headline"]
16
- end
17
-
18
- def steps
19
- @digest.steps
20
- end
21
-
22
- def step(id)
23
- @digest.step(id.to_s)
24
- end
25
-
26
- def next_step(state)
27
- shown(@digest.next_step(named(state)))
28
- end
29
-
30
- def run(progress)
31
- while (node = @digest.next_step(named(progress.recorded))) && acts?(node)
32
- progress.record(node.id, @registry.fetch(node.type).process(node, named(progress.recorded)))
33
- end
34
- end
35
-
36
- def drawing_at(state)
37
- Drawing.of(@digest.next_step(named(state)), @registry)
38
- end
39
-
40
- def state_on_path(state)
41
- @digest.state_on_path(named(state)).symbolize_keys
42
- end
43
-
44
- def steps_on_path(state)
45
- state_on_path(state).keys.map { |id| shown(@digest.step(id.to_s)) }
46
- end
47
-
48
- private
49
-
50
- def acts?(node)
51
- @registry.registered?(node.type) && @registry.fetch(node.type).acts?
52
- end
53
-
54
- def shown(node)
55
- return node unless node && @registry.registered?(node.type)
56
-
57
- @registry.fetch(node.type).display_of(node) || node
58
- end
59
-
60
- def named(state)
61
- state.transform_keys(&:to_s)
62
- end
63
- end
64
- end
65
- end
@@ -1,80 +0,0 @@
1
- module Alembic
2
- module Flow
3
- class Settings
4
- attr_reader :fields, :labels, :record_fields, :record_labels, :choices, :drawn_from, :outputs_of
5
-
6
- def initialize(fields: {}, labels: {}, record_fields: {}, record_labels: {}, choices: {},
7
- limits: {}, checks: {}, required: [], drawn_from: {}, outputs_of: {})
8
- @fields = fields
9
- @labels = labels
10
- @record_fields = record_fields
11
- @record_labels = record_labels
12
- @choices = choices
13
- @limits = limits
14
- @checks = checks
15
- @required = required
16
- @drawn_from = drawn_from
17
- @outputs_of = outputs_of
18
- end
19
-
20
- def naming_steps
21
- fields.select { |_name, type| type == :previous_step }.keys
22
- end
23
-
24
- def required
25
- fields.keys.select { |name| @required.include?(name) || naming_steps.include?(name) }
26
- end
27
-
28
- def requirements_for(config)
29
- naming_steps.map { |name| config.to_h[name.to_s] }.compact_blank
30
- end
31
-
32
- def coerce(config)
33
- config.to_h.to_h { |name, value| [ name, cast(fields[name.to_sym], value, record_fields[name.to_sym]) ] }
34
- end
35
-
36
- def objections(config)
37
- config.to_h.flat_map { |name, value| objections_to(name.to_sym, value) }.compact
38
- end
39
-
40
- private
41
-
42
- def objections_to(name, value)
43
- [ unoffered(name, value), over_limit(name, value), refused(name, value) ]
44
- end
45
-
46
- def unoffered(name, value)
47
- offered = choices[name]
48
- return unless offered
49
-
50
- stray = Array(value).find { |chosen| offered.exclude?(chosen) }
51
- "#{labels[name]} does not offer #{stray}" if stray
52
- end
53
-
54
- def over_limit(name, value)
55
- allowed = @limits[name]
56
- return unless allowed && Array(value).size > allowed
57
-
58
- "#{labels[name]} takes at most #{allowed}"
59
- end
60
-
61
- def refused(name, value)
62
- @checks[name]&.call(value)
63
- end
64
-
65
- def cast(type, value, holds = nil)
66
- case type
67
- when :integer then Integer(value, exception: false)
68
- when :float then Float(value, exception: false)
69
- when :boolean then ActiveModel::Type::Boolean.new.cast(value).present?
70
- when :list then Array(value).map { |entry| cast_entry(entry, holds.to_h) }
71
- else value
72
- end
73
- end
74
-
75
- def cast_entry(entry, holds)
76
- entry.to_h.to_h { |name, value| [ name, cast(holds[name.to_sym], value) ] }
77
- end
78
- end
79
- end
80
- end