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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e03698bc80c7779212a57aaa9bbca97b5567025b950a6a6b7170fb635745e3e0
4
- data.tar.gz: 7c3eca3a9da18aa6001791d2c5def0abacbf95b8b9baeb2be8920df804734bdd
3
+ metadata.gz: 92db6bbc2b6ecefe9836a96c3014b94cc43c48a2fb4e82152f71bea00d6b3bc4
4
+ data.tar.gz: f9a2d946f0bf343f4a20f943f2aed35d3fce8ddbc42cf28997b10f10bfa3f9ba
5
5
  SHA512:
6
- metadata.gz: a6dedb4d4413eab6222933b8096bf45c6fef7db7d51853e1a46ecaf51f4b718362bbe00776f5adfc3b3687dcf10fb6660b289d9dc930768dadfa5dc1bfbda1b9
7
- data.tar.gz: c822f36b904e46fd6075979dd3acca19ed1e91ddcf73a5adf93ca4ad261e2da5e5507866fe890641c116ecb4695e25b16281fd5aa54304b7328b50b5a4d55b34
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 = setup_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
- add_columns
34
- add_rows
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 ||= @routes.map(&:row).max + 1
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 add_columns
133
+ def set_column_numbers
125
134
  @routes.each do |route|
126
- route.flow_uuids.each.with_index(route.column) do |uuid, column|
127
- column_number = @coordinates[uuid][:column]
128
- if column_number.nil? || column > column_number
129
- @coordinates[uuid][:column] = column
130
- end
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 add_rows
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
- @coordinates[uuid][:row] = route.row if @coordinates[uuid][:row].nil?
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
- @coordinates.each do |uuid, position|
191
+ service.flow.each_key do |uuid|
192
+ position = coordinates.position(uuid)
165
193
  next if detached?(position)
166
194
 
167
- @ordered[position[:column]][position[:row]] = get_flow_object(uuid)
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
- position = @coordinates[branch.uuid]
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
- uuids.push(uuid) unless uuids.include?(uuid)
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
- # The frontend requires that expressions of type 'or' get there own line and
238
- # arrow. 'and' expression types continue to be grouped together.
239
- # Return the UUIDs of the destinations exiting a branch and allow duplicates
240
- # if the expression type is an 'or'.
241
- def exiting_destinations_from_branch(branch)
242
- destination_uuids = branch.conditionals.map do |conditional|
243
- if conditional.type == 'or'
244
- conditional.expressions.map(&:next)
245
- else
246
- conditional.next
247
- end
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