metadata_presenter 2.4.1 → 2.6.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/models/metadata_presenter/flow.rb +4 -0
- data/app/models/metadata_presenter/grid.rb +203 -0
- data/app/models/metadata_presenter/route.rb +76 -0
- data/app/views/metadata_presenter/page/exit.html.erb +29 -0
- data/config/initializers/supported_components.rb +4 -0
- data/default_metadata/page/exit.json +9 -0
- data/fixtures/branching_2.json +686 -0
- data/fixtures/branching_2.png +0 -0
- data/fixtures/branching_3.json +539 -0
- data/fixtures/branching_3.png +0 -0
- data/fixtures/branching_4.json +557 -0
- data/fixtures/branching_4.png +0 -0
- data/fixtures/branching_5.json +622 -0
- data/fixtures/branching_5.png +0 -0
- data/fixtures/branching_6.json +493 -0
- data/fixtures/branching_6.png +0 -0
- data/fixtures/branching_7.json +614 -0
- data/fixtures/branching_7.png +0 -0
- data/fixtures/branching_8.json +560 -0
- data/fixtures/branching_8.png +0 -0
- data/lib/metadata_presenter/version.rb +1 -1
- data/lib/metadata_presenter.rb +1 -0
- data/schemas/page/exit.json +25 -0
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e206021a09261b7ed5628f794767a935f94c89315a9a2da0771e0e8b0a3f7d10
|
4
|
+
data.tar.gz: 82fdc1a08806381b692198e17f1d9e600d876a8ee1313947738ffbfb9585c2c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cd644e56226d865e3f1d903e06b0b82bb606bb981f6b32510670a5d2ae5e262e890c9ff5d65e77796c07715e184b4fb772e2a3cd4a648c851c558d6aa12eea9
|
7
|
+
data.tar.gz: 75524d74b558b8f64b9cd31f3c2125ba94df8744e6ca7afa9ab49f0c499f29c91bbe2428c81f61f12e35c3156539b122f921637280b8a97d84bb8afff7f615dd
|
@@ -16,6 +16,10 @@ module MetadataPresenter
|
|
16
16
|
metadata['next']['default']
|
17
17
|
end
|
18
18
|
|
19
|
+
def all_destination_uuids
|
20
|
+
conditionals.map(&:next).append(default_next)
|
21
|
+
end
|
22
|
+
|
19
23
|
def conditionals
|
20
24
|
Array(metadata['next']['conditionals']).map do |conditional_metadata|
|
21
25
|
Conditional.new(conditional_metadata)
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class Spacer < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class Grid
|
6
|
+
def initialize(service)
|
7
|
+
@service = service
|
8
|
+
@ordered = []
|
9
|
+
@routes = []
|
10
|
+
@traversed = []
|
11
|
+
@coordinates = setup_coordinates
|
12
|
+
end
|
13
|
+
|
14
|
+
ROW_ZERO = 0
|
15
|
+
|
16
|
+
def build
|
17
|
+
return @ordered unless @ordered.empty?
|
18
|
+
|
19
|
+
@ordered = make_grid
|
20
|
+
add_columns
|
21
|
+
add_rows
|
22
|
+
add_by_coordinates
|
23
|
+
insert_expression_spacers
|
24
|
+
trim_spacers
|
25
|
+
|
26
|
+
@ordered
|
27
|
+
end
|
28
|
+
|
29
|
+
def ordered_flow
|
30
|
+
@ordered_flow ||=
|
31
|
+
build.flatten.reject { |obj| obj.is_a?(MetadataPresenter::Spacer) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def ordered_pages
|
35
|
+
ordered_flow.reject(&:branch?)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :service
|
41
|
+
attr_accessor :ordered, :traversed, :routes, :coordinates
|
42
|
+
|
43
|
+
def setup_coordinates
|
44
|
+
service.flow.keys.index_with { |_uuid| { row: nil, column: nil } }
|
45
|
+
end
|
46
|
+
|
47
|
+
def route_from_start
|
48
|
+
@route_from_start ||=
|
49
|
+
MetadataPresenter::Route.new(
|
50
|
+
service: service,
|
51
|
+
traverse_from: service.start_page.uuid
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def make_grid
|
56
|
+
traverse_all_routes
|
57
|
+
|
58
|
+
rows = @routes.map(&:row).max
|
59
|
+
columns = @routes.map { |r| r.column + r.flow_uuids.count }.max
|
60
|
+
columns.times.map { rows.times.map { MetadataPresenter::Spacer.new } }
|
61
|
+
end
|
62
|
+
|
63
|
+
def traverse_all_routes
|
64
|
+
# Always traverse the route that begins from the start page first and get
|
65
|
+
# the potential routes from any branching points that exist.
|
66
|
+
route_from_start.traverse
|
67
|
+
@routes.append(route_from_start)
|
68
|
+
traversed_routes = route_from_start.routes
|
69
|
+
|
70
|
+
index = 0
|
71
|
+
until traversed_routes.empty?
|
72
|
+
if index > total_potential_routes
|
73
|
+
ActiveSupport::Notifications.instrument(
|
74
|
+
'exceeded_total_potential_routes',
|
75
|
+
message: 'Exceeded total number of potential routes'
|
76
|
+
)
|
77
|
+
break
|
78
|
+
end
|
79
|
+
|
80
|
+
route = traversed_routes.shift
|
81
|
+
@routes.append(route)
|
82
|
+
|
83
|
+
# Every route exiting a branching point needs to be traversed and any
|
84
|
+
# additional routes from other branching points collected and then also
|
85
|
+
# traversed.
|
86
|
+
route.traverse
|
87
|
+
traversed_routes |= route.routes
|
88
|
+
|
89
|
+
index += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_columns
|
94
|
+
@routes.each do |route|
|
95
|
+
route.flow_uuids.each.with_index(route.column) do |uuid, column|
|
96
|
+
column_number = @coordinates[uuid][:column]
|
97
|
+
if column_number.nil? || column > column_number
|
98
|
+
@coordinates[uuid][:column] = column
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_rows
|
105
|
+
@routes.each do |route|
|
106
|
+
route.flow_uuids.each do |uuid|
|
107
|
+
next if @traversed.include?(uuid)
|
108
|
+
|
109
|
+
@coordinates[uuid][:row] = route.row if @coordinates[uuid][:row].nil?
|
110
|
+
|
111
|
+
update_route_rows(route, uuid)
|
112
|
+
@traversed.push(uuid)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Each Route object has a starting row. Each Route object has no knowledge
|
118
|
+
# of other potential routes and pages/branches that may or may not exist in
|
119
|
+
# them. The starting row may need to change dependent upon what has been
|
120
|
+
# traversed in other routes.
|
121
|
+
def update_route_rows(route, uuid)
|
122
|
+
flow_object = service.flow_object(uuid)
|
123
|
+
if flow_object.branch? && route.row > ROW_ZERO
|
124
|
+
destinations = routes_exiting_branch(flow_object)
|
125
|
+
destinations.each.with_index(route.row) do |destination_uuid, row|
|
126
|
+
@routes.each do |r|
|
127
|
+
r.row = row if r.traverse_from == destination_uuid
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def add_by_coordinates
|
134
|
+
@coordinates.each do |uuid, position|
|
135
|
+
# If row and column are nil then the object is detached
|
136
|
+
next if position[:row].nil? || position[:column].nil?
|
137
|
+
|
138
|
+
flow_object = service.flow_object(uuid)
|
139
|
+
@ordered[position[:column]][position[:row]] = flow_object
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Find the very last MetadataPresenter::Flow object in every column and
|
144
|
+
# remove any Spacer objects after that.
|
145
|
+
def trim_spacers
|
146
|
+
@ordered.each_with_index do |column, index|
|
147
|
+
last_index_of = column.rindex { |item| item.is_a?(MetadataPresenter::Flow) }
|
148
|
+
@ordered[index] = @ordered[index][0..last_index_of]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Each branch has a certain number of exits that require their own line
|
153
|
+
# and arrow. Insert any spacers into the necessary row in the column after
|
154
|
+
# the one the branch is located in.
|
155
|
+
def insert_expression_spacers
|
156
|
+
service.branches.each do |branch|
|
157
|
+
position = @coordinates[branch.uuid]
|
158
|
+
next if position[:row].nil? || position[:column].nil? # detached branch
|
159
|
+
|
160
|
+
next_column = position[:column] + 1
|
161
|
+
uuids = []
|
162
|
+
exiting_destinations_from_branch(branch).each.with_index(position[:row]) do |uuid, row|
|
163
|
+
if uuids.include?(uuid)
|
164
|
+
@ordered[next_column].insert(row, MetadataPresenter::Spacer.new)
|
165
|
+
end
|
166
|
+
|
167
|
+
uuids.push(uuid) unless uuids.include?(uuid)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# The frontend requires that expressions of type 'or' get there own line and
|
173
|
+
# arrow. 'and' expression types continue to be grouped together.
|
174
|
+
# Return the UUIDs of the destinations exiting a branch and allow duplicates
|
175
|
+
# if the expression type is an 'or'.
|
176
|
+
def exiting_destinations_from_branch(branch)
|
177
|
+
destination_uuids = branch.conditionals.map do |conditional|
|
178
|
+
if conditional.type == 'or'
|
179
|
+
conditional.expressions.map(&:next)
|
180
|
+
else
|
181
|
+
conditional.next
|
182
|
+
end
|
183
|
+
end
|
184
|
+
destination_uuids.flatten
|
185
|
+
end
|
186
|
+
|
187
|
+
# Any destinations exiting the branch that have not already been traversed.
|
188
|
+
def routes_exiting_branch(branch)
|
189
|
+
branch.all_destination_uuids.reject { |uuid| @traversed.include?(uuid) }
|
190
|
+
end
|
191
|
+
|
192
|
+
# Deliberately not including the default next for each branch as when row
|
193
|
+
# zero is created it takes the first available conditional for each branch.
|
194
|
+
# The remaining are then used to create route objects. Therefore the total
|
195
|
+
# number of remaining routes will be the same as the total of all the branch
|
196
|
+
# conditionals.
|
197
|
+
# Add 1 additional route as that represents the route_from_start.
|
198
|
+
def total_potential_routes
|
199
|
+
@total_potential_routes ||=
|
200
|
+
service.branches.sum { |branch| branch.conditionals.size } + 1
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class Route
|
3
|
+
attr_reader :traverse_from
|
4
|
+
attr_accessor :flow_uuids, :routes, :row, :column
|
5
|
+
|
6
|
+
def initialize(service:, traverse_from:, row: 0, column: 0)
|
7
|
+
@service = service
|
8
|
+
@traverse_from = traverse_from
|
9
|
+
@row = row
|
10
|
+
@column = column
|
11
|
+
@routes = []
|
12
|
+
@flow_uuids = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def traverse
|
16
|
+
@flow_uuid = traverse_from
|
17
|
+
|
18
|
+
index = column
|
19
|
+
until @flow_uuid.blank?
|
20
|
+
if index > service.flow.size
|
21
|
+
ActiveSupport::Notifications.instrument(
|
22
|
+
'exceeded_total_flow_objects',
|
23
|
+
message: 'Exceeded total number of flow objects'
|
24
|
+
)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
|
28
|
+
@flow_uuids.push(@flow_uuid) unless @flow_uuids.include?(@flow_uuid)
|
29
|
+
flow_object = service.flow_object(@flow_uuid)
|
30
|
+
|
31
|
+
if flow_object.branch?
|
32
|
+
destinations = destination_uuids(flow_object)
|
33
|
+
# Take the first conditional destination and follow that until the end
|
34
|
+
# of the route.
|
35
|
+
@flow_uuid = destinations.shift
|
36
|
+
|
37
|
+
# The remaining conditional destinations and the branch's default next
|
38
|
+
# (otherwise) will be the starting point for a new Route object to be
|
39
|
+
# traversed.
|
40
|
+
# The default behaviour is that the next destination will be on the row
|
41
|
+
# below this current route's row. This can be changed under certain
|
42
|
+
# conditions in the Grid model.
|
43
|
+
# Each of the destinations need to be placed in the column after the
|
44
|
+
# current column.
|
45
|
+
row_number = row + 1
|
46
|
+
column_number = index + 1
|
47
|
+
destinations.each do |uuid|
|
48
|
+
@routes.push(
|
49
|
+
MetadataPresenter::Route.new(
|
50
|
+
service: service,
|
51
|
+
traverse_from: uuid,
|
52
|
+
row: row_number,
|
53
|
+
column: column_number
|
54
|
+
)
|
55
|
+
)
|
56
|
+
row_number += 1
|
57
|
+
end
|
58
|
+
else
|
59
|
+
@flow_uuid = flow_object.default_next
|
60
|
+
end
|
61
|
+
|
62
|
+
index += 1
|
63
|
+
end
|
64
|
+
|
65
|
+
@flow_uuids
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_reader :service
|
71
|
+
|
72
|
+
def destination_uuids(flow_object)
|
73
|
+
flow_object.conditionals.map(&:next).push(flow_object.default_next)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="fb-main-grid-wrapper" data-fb-pagetype="<%= @page.type %>">
|
2
|
+
<div class="govuk-grid-row">
|
3
|
+
<div class="govuk-grid-column-two-thirds">
|
4
|
+
|
5
|
+
<%= render 'metadata_presenter/attribute/section_heading' %>
|
6
|
+
|
7
|
+
<h1 class="fb-editable govuk-heading-xl"
|
8
|
+
data-fb-content-id="page[heading]"
|
9
|
+
data-fb-content-type="element">
|
10
|
+
<%= @page.heading %>
|
11
|
+
</h1>
|
12
|
+
|
13
|
+
<%= render 'metadata_presenter/attribute/lede' %>
|
14
|
+
|
15
|
+
<%= form_for @page_answers, as: :answers, url: @page.url, method: :post do |f| %>
|
16
|
+
|
17
|
+
<%= render partial: 'metadata_presenter/component/components',
|
18
|
+
locals: {
|
19
|
+
f: f,
|
20
|
+
components: @page.components,
|
21
|
+
tag: nil,
|
22
|
+
classes: nil,
|
23
|
+
input_components: @page.supported_input_components,
|
24
|
+
content_components: @page.supported_content_components
|
25
|
+
} %>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</div>
|
@@ -12,6 +12,10 @@ Rails.application.config.supported_components =
|
|
12
12
|
input: %w(),
|
13
13
|
content: %w(content)
|
14
14
|
},
|
15
|
+
exit: {
|
16
|
+
input: %w(),
|
17
|
+
content: %w(content)
|
18
|
+
},
|
15
19
|
multiplequestions: {
|
16
20
|
input: %w(text textarea number date radios checkboxes),
|
17
21
|
content: %w(content)
|