metadata_presenter 2.10.0 → 2.13.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/branch_destinations.rb +18 -0
- data/app/models/metadata_presenter/column_number.rb +21 -0
- data/app/models/metadata_presenter/coordinates.rb +51 -0
- data/app/models/metadata_presenter/grid.rb +111 -42
- data/app/models/metadata_presenter/row_number.rb +82 -0
- data/app/models/metadata_presenter/service.rb +7 -1
- data/fixtures/branching_10.json +1271 -0
- data/fixtures/branching_10.png +0 -0
- data/fixtures/branching_7.json +0 -62
- data/lib/metadata_presenter/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92db6bbc2b6ecefe9836a96c3014b94cc43c48a2fb4e82152f71bea00d6b3bc4
|
4
|
+
data.tar.gz: f9a2d946f0bf343f4a20f943f2aed35d3fce8ddbc42cf28997b10f10bfa3f9ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b94a4afee1f43a692360645775bb5555c3c722f93a5c1f2c0cb865af90e43448d5eb1bab458eb022c49d76a39348497f2946ca925afdf11fd8ed5f8749ef0f4
|
7
|
+
data.tar.gz: 10ad22eada3869215e8d7ef9e00dc578468d142b7c3d151c9ba8590ad001101570e317f1b7b1f2dd5343572608ba4e1464383f8c57acbb317aa160cd2ab85157
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
module BranchDestinations
|
3
|
+
# The frontend requires that expressions of type 'or' get there own line and
|
4
|
+
# arrow. 'and' expression types continue to be grouped together.
|
5
|
+
# Return the UUIDs of the destinations exiting a branch and allow duplicates
|
6
|
+
# if the expression type is an 'or'.
|
7
|
+
def exiting_destinations_from_branch(branch)
|
8
|
+
destination_uuids = branch.conditionals.map do |conditional|
|
9
|
+
if conditional.type == 'or'
|
10
|
+
conditional.expressions.map { |_| conditional.next }
|
11
|
+
else
|
12
|
+
conditional.next
|
13
|
+
end
|
14
|
+
end
|
15
|
+
destination_uuids.flatten.push(branch.default_next)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class ColumnNumber
|
3
|
+
def initialize(uuid:, coordinates:, new_column:)
|
4
|
+
@uuid = uuid
|
5
|
+
@coordinates = coordinates
|
6
|
+
@new_column = new_column
|
7
|
+
end
|
8
|
+
|
9
|
+
def number
|
10
|
+
[existing_column, new_column].compact.max
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :uuid, :coordinates, :new_column
|
16
|
+
|
17
|
+
def existing_column
|
18
|
+
@coordinates.uuid_column(uuid)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class Coordinates
|
3
|
+
def initialize(flow)
|
4
|
+
@flow = flow
|
5
|
+
@positions = setup_positions
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :positions
|
9
|
+
|
10
|
+
def set_column(uuid, number)
|
11
|
+
positions[uuid][:column] = number
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_row(uuid, number)
|
15
|
+
positions[uuid][:row] = number
|
16
|
+
end
|
17
|
+
|
18
|
+
def uuid_column(uuid)
|
19
|
+
positions[uuid][:column]
|
20
|
+
end
|
21
|
+
|
22
|
+
def uuid_row(uuid)
|
23
|
+
positions[uuid][:row]
|
24
|
+
end
|
25
|
+
|
26
|
+
def uuid_at_position(column, row)
|
27
|
+
positions.find do |uuid, position|
|
28
|
+
if position[:column] == column && position[:row] == row
|
29
|
+
return uuid
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def position(uuid)
|
35
|
+
positions[uuid]
|
36
|
+
end
|
37
|
+
|
38
|
+
def positions_in_column(column_number)
|
39
|
+
positions.select { |_, position| position[:column] == column_number }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :flow
|
45
|
+
attr_writer :positions
|
46
|
+
|
47
|
+
def setup_positions
|
48
|
+
flow.keys.index_with { |_uuid| { row: nil, column: nil } }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -11,7 +11,14 @@ module MetadataPresenter
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
class Warning < OpenStruct
|
15
|
+
def type
|
16
|
+
'flow.warning'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
14
20
|
class Grid
|
21
|
+
include BranchDestinations
|
15
22
|
attr_reader :start_from
|
16
23
|
|
17
24
|
def initialize(service, start_from: nil, main_flow: [])
|
@@ -21,7 +28,7 @@ module MetadataPresenter
|
|
21
28
|
@ordered = []
|
22
29
|
@routes = []
|
23
30
|
@traversed = []
|
24
|
-
@coordinates =
|
31
|
+
@coordinates = MetadataPresenter::Coordinates.new(service.flow)
|
25
32
|
end
|
26
33
|
|
27
34
|
ROW_ZERO = 0
|
@@ -30,19 +37,20 @@ module MetadataPresenter
|
|
30
37
|
return @ordered unless @ordered.empty?
|
31
38
|
|
32
39
|
@ordered = make_grid
|
33
|
-
|
34
|
-
|
40
|
+
set_column_numbers
|
41
|
+
set_row_numbers
|
35
42
|
add_by_coordinates
|
36
43
|
insert_expression_spacers
|
37
44
|
trim_pointers unless main_flow.empty?
|
38
45
|
trim_spacers
|
46
|
+
insert_warning if main_flow.empty?
|
39
47
|
|
40
48
|
@ordered = @ordered.reject(&:empty?)
|
41
49
|
end
|
42
50
|
|
43
51
|
def ordered_flow
|
44
52
|
@ordered_flow ||=
|
45
|
-
build.flatten.reject { |obj| obj.is_a?(MetadataPresenter::Spacer) }
|
53
|
+
build.flatten.reject { |obj| obj.is_a?(MetadataPresenter::Spacer) || obj.is_a?(MetadataPresenter::Warning) }
|
46
54
|
end
|
47
55
|
|
48
56
|
def ordered_pages
|
@@ -62,10 +70,6 @@ module MetadataPresenter
|
|
62
70
|
attr_reader :service, :main_flow
|
63
71
|
attr_accessor :ordered, :traversed, :routes, :coordinates
|
64
72
|
|
65
|
-
def setup_coordinates
|
66
|
-
service.flow.keys.index_with { |_uuid| { row: nil, column: nil } }
|
67
|
-
end
|
68
|
-
|
69
73
|
def route_from_start
|
70
74
|
@route_from_start ||=
|
71
75
|
MetadataPresenter::Route.new(
|
@@ -83,7 +87,12 @@ module MetadataPresenter
|
|
83
87
|
end
|
84
88
|
|
85
89
|
def max_potential_rows
|
86
|
-
@max_potential_rows ||=
|
90
|
+
@max_potential_rows ||= begin
|
91
|
+
destinations_count = service.branches.map do |branch|
|
92
|
+
exiting_destinations_from_branch(branch).count
|
93
|
+
end
|
94
|
+
destinations_count.sum
|
95
|
+
end
|
87
96
|
end
|
88
97
|
|
89
98
|
def max_potential_columns
|
@@ -121,29 +130,47 @@ module MetadataPresenter
|
|
121
130
|
end
|
122
131
|
end
|
123
132
|
|
124
|
-
def
|
133
|
+
def set_column_numbers
|
125
134
|
@routes.each do |route|
|
126
|
-
route.flow_uuids.each.with_index(route.column) do |uuid,
|
127
|
-
column_number =
|
128
|
-
|
129
|
-
|
130
|
-
|
135
|
+
route.flow_uuids.each.with_index(route.column) do |uuid, new_column|
|
136
|
+
column_number = MetadataPresenter::ColumnNumber.new(
|
137
|
+
uuid: uuid,
|
138
|
+
new_column: new_column,
|
139
|
+
coordinates: @coordinates
|
140
|
+
).number
|
141
|
+
@coordinates.set_column(uuid, column_number)
|
131
142
|
end
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
135
|
-
def
|
146
|
+
def set_row_numbers
|
136
147
|
@routes.each do |route|
|
137
|
-
next if @traversed.include?(route.traverse_from)
|
148
|
+
next if @traversed.include?(route.traverse_from) && appears_later_in_flow?(route)
|
138
149
|
|
150
|
+
current_row = route.row
|
139
151
|
route.flow_uuids.each do |uuid|
|
140
|
-
|
152
|
+
row_number = MetadataPresenter::RowNumber.new(
|
153
|
+
uuid: uuid,
|
154
|
+
route: route,
|
155
|
+
current_row: current_row,
|
156
|
+
coordinates: @coordinates,
|
157
|
+
service: service
|
158
|
+
).number
|
159
|
+
@coordinates.set_row(uuid, row_number)
|
160
|
+
|
141
161
|
update_route_rows(route, uuid)
|
142
162
|
@traversed.push(uuid) unless @traversed.include?(uuid)
|
163
|
+
current_row = row_number
|
143
164
|
end
|
144
165
|
end
|
145
166
|
end
|
146
167
|
|
168
|
+
# New routes can be linked to later. We need to also traverse these to see
|
169
|
+
# if anything should be moved to a different row.
|
170
|
+
def appears_later_in_flow?(route)
|
171
|
+
@coordinates.uuid_column(route.traverse_from) > route.column
|
172
|
+
end
|
173
|
+
|
147
174
|
# Each Route object has a starting row. Each Route object has no knowledge
|
148
175
|
# of other potential routes and pages/branches that may or may not exist in
|
149
176
|
# them. The starting row may need to change dependent upon what has been
|
@@ -161,10 +188,14 @@ module MetadataPresenter
|
|
161
188
|
end
|
162
189
|
|
163
190
|
def add_by_coordinates
|
164
|
-
|
191
|
+
service.flow.each_key do |uuid|
|
192
|
+
position = coordinates.position(uuid)
|
165
193
|
next if detached?(position)
|
166
194
|
|
167
|
-
|
195
|
+
column = position[:column]
|
196
|
+
row = position[:row]
|
197
|
+
insert_spacer(column, row) if occupied?(column, row, uuid)
|
198
|
+
@ordered[column][row] = get_flow_object(uuid)
|
168
199
|
end
|
169
200
|
end
|
170
201
|
|
@@ -172,6 +203,11 @@ module MetadataPresenter
|
|
172
203
|
position[:row].nil? || position[:column].nil?
|
173
204
|
end
|
174
205
|
|
206
|
+
def occupied?(column, row, uuid)
|
207
|
+
object = @ordered[column][row]
|
208
|
+
object.is_a?(MetadataPresenter::Flow) && object.uuid != uuid
|
209
|
+
end
|
210
|
+
|
175
211
|
def get_flow_object(uuid)
|
176
212
|
# main_flow is always empty if the Grid is _actually_ building the main flow
|
177
213
|
return MetadataPresenter::Pointer.new(uuid: uuid) if main_flow.include?(uuid)
|
@@ -219,37 +255,70 @@ module MetadataPresenter
|
|
219
255
|
# the one the branch is located in.
|
220
256
|
def insert_expression_spacers
|
221
257
|
service.branches.each do |branch|
|
222
|
-
|
223
|
-
next if position[:row].nil? || position[:column].nil? # detached branch
|
224
|
-
|
225
|
-
next_column = position[:column] + 1
|
226
|
-
uuids = []
|
227
|
-
exiting_destinations_from_branch(branch).each.with_index(position[:row]) do |uuid, row|
|
228
|
-
if uuids.include?(uuid)
|
229
|
-
@ordered[next_column].insert(row, MetadataPresenter::Spacer.new)
|
230
|
-
end
|
258
|
+
next if coordinates.uuid_column(branch.uuid).nil?
|
231
259
|
|
232
|
-
|
260
|
+
previous_uuid = ''
|
261
|
+
next_column = coordinates.uuid_column(branch.uuid) + 1
|
262
|
+
exiting_destinations_from_branch(branch).each_with_index do |uuid, row|
|
263
|
+
insert_spacer(next_column, row) if uuid == previous_uuid
|
264
|
+
previous_uuid = uuid
|
233
265
|
end
|
234
266
|
end
|
235
267
|
end
|
236
268
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
269
|
+
def insert_spacer(column, row)
|
270
|
+
@ordered[column].insert(row, MetadataPresenter::Spacer.new)
|
271
|
+
end
|
272
|
+
|
273
|
+
# Include a warning if a service does not have a CYA or Confirmation page in the
|
274
|
+
# main flow. The warning should always be in the first row, last column.
|
275
|
+
def insert_warning
|
276
|
+
if cya_and_confirmation_pages_not_in_service? ||
|
277
|
+
cya_and_confirmation_pages_detached?
|
278
|
+
@ordered.append([MetadataPresenter::Warning.new])
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def cya_and_confirmation_pages_not_in_service?
|
283
|
+
(checkanswers_not_in_service? && confirmation_not_in_service?) ||
|
284
|
+
checkanswers_not_in_service? ||
|
285
|
+
confirmation_not_in_service?
|
286
|
+
end
|
287
|
+
|
288
|
+
def checkanswers_not_in_service?
|
289
|
+
service.checkanswers_page.blank?
|
290
|
+
end
|
291
|
+
|
292
|
+
def confirmation_not_in_service?
|
293
|
+
service.confirmation_page.blank?
|
294
|
+
end
|
295
|
+
|
296
|
+
def cya_and_confirmation_pages_detached?
|
297
|
+
(checkanswers_detached? && confirmation_detached?) ||
|
298
|
+
checkanswers_detached? ||
|
299
|
+
confirmation_detached?
|
300
|
+
end
|
301
|
+
|
302
|
+
def checkanswers_detached?
|
303
|
+
if service.checkanswers_page.present?
|
304
|
+
uuid = service.checkanswers_page.uuid
|
305
|
+
position = coordinates.position(uuid)
|
306
|
+
detached?(position)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def confirmation_detached?
|
311
|
+
if service.confirmation_page.present?
|
312
|
+
uuid = service.confirmation_page.uuid
|
313
|
+
position = coordinates.position(uuid)
|
314
|
+
detached?(position)
|
248
315
|
end
|
249
|
-
destination_uuids.flatten
|
250
316
|
end
|
251
317
|
|
252
318
|
# Any destinations exiting the branch that have not already been traversed.
|
319
|
+
# This removes any branch destinations that already exist on other rows. If
|
320
|
+
# that is the case then the arrow will flow towards whatever row that object
|
321
|
+
# is located.
|
253
322
|
def routes_exiting_branch(branch)
|
254
323
|
branch.all_destination_uuids.reject { |uuid| @traversed.include?(uuid) }
|
255
324
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class RowNumber
|
3
|
+
include BranchDestinations
|
4
|
+
|
5
|
+
def initialize(uuid:, route:, current_row:, coordinates:, service:)
|
6
|
+
@uuid = uuid
|
7
|
+
@route = route
|
8
|
+
@current_row = current_row
|
9
|
+
@coordinates = coordinates
|
10
|
+
@service = service
|
11
|
+
end
|
12
|
+
|
13
|
+
ROW_ZERO = 0
|
14
|
+
|
15
|
+
def number
|
16
|
+
return route.row if first_row? && existing_row.nil?
|
17
|
+
|
18
|
+
return ROW_ZERO if place_on_row_zero?
|
19
|
+
|
20
|
+
[current_row, existing_row, potential_row].compact.max
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :uuid, :route, :current_row, :coordinates, :service
|
26
|
+
|
27
|
+
def existing_row
|
28
|
+
@existing_row ||= coordinates.uuid_row(uuid)
|
29
|
+
end
|
30
|
+
|
31
|
+
def potential_row
|
32
|
+
return unless object_above.branch? && uuid != object_above.uuid
|
33
|
+
|
34
|
+
coordinates.uuid_row(object_above.uuid) + number_of_destinations
|
35
|
+
end
|
36
|
+
|
37
|
+
def first_row?
|
38
|
+
@first_row ||= route.row.zero?
|
39
|
+
end
|
40
|
+
|
41
|
+
def object_above
|
42
|
+
@object_above ||=
|
43
|
+
service.flow_object(
|
44
|
+
coordinates.uuid_at_position(uuid_column, row_number_for_object_above)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def row_number_for_object_above
|
49
|
+
column_objects.map { |_, p| p[:row] if p[:row] < current_row }.compact.max.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
def column_objects
|
53
|
+
objects_in_column = coordinates.positions_in_column(uuid_column).reject do |u, p|
|
54
|
+
u == uuid || p[:row].nil?
|
55
|
+
end
|
56
|
+
objects_in_column.sort_by { |_, p| p[:row] }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Takes into account the 'or' type of conditionals which requires an
|
60
|
+
# additional spacer
|
61
|
+
def number_of_destinations
|
62
|
+
exiting_destinations_from_branch(object_above).count
|
63
|
+
end
|
64
|
+
|
65
|
+
def uuid_column
|
66
|
+
@uuid_column ||= coordinates.uuid_column(uuid)
|
67
|
+
end
|
68
|
+
|
69
|
+
# If an object has already been positioned on row 0 then leave it there.
|
70
|
+
# If the object is a checkanswers or confirmation type then always place it
|
71
|
+
# on row 0.
|
72
|
+
def place_on_row_zero?
|
73
|
+
cya_or_confirmation_page? || coordinates.uuid_row(uuid) == ROW_ZERO
|
74
|
+
end
|
75
|
+
|
76
|
+
def cya_or_confirmation_page?
|
77
|
+
%w[page.checkanswers page.confirmation].include?(
|
78
|
+
service.find_page_by_uuid(uuid)&.type
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -38,7 +38,7 @@ class MetadataPresenter::Service < MetadataPresenter::Metadata
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def service_slug
|
41
|
-
service_name.parameterize
|
41
|
+
service_name.gsub(/['’]/, '').parameterize
|
42
42
|
end
|
43
43
|
|
44
44
|
def find_page_by_url(url)
|
@@ -60,6 +60,12 @@ class MetadataPresenter::Service < MetadataPresenter::Metadata
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
def checkanswers_page
|
64
|
+
@checkanswers_page ||= pages.find do |page|
|
65
|
+
page.type == 'page.checkanswers'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
63
69
|
def meta
|
64
70
|
MetadataPresenter::Meta.new(configuration['meta'])
|
65
71
|
end
|