metadata_presenter 2.9.0 → 2.12.1

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: 2456166b18ccc3e41ec5387fc88a53643c6ae377caf938c5f30c14684ba5f7aa
4
- data.tar.gz: 15503786a56b715ba5001288e3f75bb98a06eeafc0629eba3eb4b7d1df420c3c
3
+ metadata.gz: 8a349beb4173c1f6361b321a95e36a407c5f5361153403cae2fbd15ffb7b8840
4
+ data.tar.gz: 8e06745f55b04f1074a5da27b77e4f9cb720b95bfb328672c14cca059e4a3003
5
5
  SHA512:
6
- metadata.gz: 542588576d0a629d453e7172340bdb5a6e4b5db091f9a48f7f8e058eec3a143cedc22d1da744580078636d17902302ef100d0e7acaa54eeff3ad82ff792f44c7
7
- data.tar.gz: 271167513a3fda4ec24f93e0b07137902c30fd6fbac05fc7fe3683422fb0fdbd8a4c6a34fd0fe5fbf797543a855fca236160e63233beae0b4fd902b8ff553d05
6
+ metadata.gz: 01f7f93e1b0d7a058ff856cb27ce07ec816b060e2b2299cec55be298aa046809a9e9ba82f9f770205b4fe647a8d3e046e1a0b10afad0a78fc5190e52aa28fc5f
7
+ data.tar.gz: 5cb518f2fb78f26ae06d447d0d1a7a7f2b84de9b4f24057079464bdcd77945c36430c4718c49d1bc2a04ec48ab1cfb422fbfc02c952a516ca83d7cae3e29a2ff
@@ -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
@@ -12,6 +12,7 @@ module MetadataPresenter
12
12
  end
13
13
 
14
14
  class Grid
15
+ include BranchDestinations
15
16
  attr_reader :start_from
16
17
 
17
18
  def initialize(service, start_from: nil, main_flow: [])
@@ -21,7 +22,7 @@ module MetadataPresenter
21
22
  @ordered = []
22
23
  @routes = []
23
24
  @traversed = []
24
- @coordinates = setup_coordinates
25
+ @coordinates = MetadataPresenter::Coordinates.new(service.flow)
25
26
  end
26
27
 
27
28
  ROW_ZERO = 0
@@ -30,8 +31,8 @@ module MetadataPresenter
30
31
  return @ordered unless @ordered.empty?
31
32
 
32
33
  @ordered = make_grid
33
- add_columns
34
- add_rows
34
+ set_column_numbers
35
+ set_row_numbers
35
36
  add_by_coordinates
36
37
  insert_expression_spacers
37
38
  trim_pointers unless main_flow.empty?
@@ -62,10 +63,6 @@ module MetadataPresenter
62
63
  attr_reader :service, :main_flow
63
64
  attr_accessor :ordered, :traversed, :routes, :coordinates
64
65
 
65
- def setup_coordinates
66
- service.flow.keys.index_with { |_uuid| { row: nil, column: nil } }
67
- end
68
-
69
66
  def route_from_start
70
67
  @route_from_start ||=
71
68
  MetadataPresenter::Route.new(
@@ -83,7 +80,12 @@ module MetadataPresenter
83
80
  end
84
81
 
85
82
  def max_potential_rows
86
- @max_potential_rows ||= @routes.map(&:row).max + 1
83
+ @max_potential_rows ||= begin
84
+ destinations_count = service.branches.map do |branch|
85
+ exiting_destinations_from_branch(branch).count
86
+ end
87
+ destinations_count.sum
88
+ end
87
89
  end
88
90
 
89
91
  def max_potential_columns
@@ -121,30 +123,47 @@ module MetadataPresenter
121
123
  end
122
124
  end
123
125
 
124
- def add_columns
126
+ def set_column_numbers
125
127
  @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
128
+ route.flow_uuids.each.with_index(route.column) do |uuid, new_column|
129
+ column_number = MetadataPresenter::ColumnNumber.new(
130
+ uuid: uuid,
131
+ new_column: new_column,
132
+ coordinates: @coordinates
133
+ ).number
134
+ @coordinates.set_column(uuid, column_number)
131
135
  end
132
136
  end
133
137
  end
134
138
 
135
- def add_rows
139
+ def set_row_numbers
136
140
  @routes.each do |route|
137
- route.flow_uuids.each do |uuid|
138
- next if @traversed.include?(uuid)
141
+ next if @traversed.include?(route.traverse_from) && appears_later_in_flow?(route)
139
142
 
140
- @coordinates[uuid][:row] = route.row if @coordinates[uuid][:row].nil?
143
+ current_row = route.row
144
+ route.flow_uuids.each do |uuid|
145
+ row_number = MetadataPresenter::RowNumber.new(
146
+ uuid: uuid,
147
+ route: route,
148
+ current_row: current_row,
149
+ coordinates: @coordinates,
150
+ service: service
151
+ ).number
152
+ @coordinates.set_row(uuid, row_number)
141
153
 
142
154
  update_route_rows(route, uuid)
143
- @traversed.push(uuid)
155
+ @traversed.push(uuid) unless @traversed.include?(uuid)
156
+ current_row = row_number
144
157
  end
145
158
  end
146
159
  end
147
160
 
161
+ # New routes can be linked to later. We need to also traverse these to see
162
+ # if anything should be moved to a different row.
163
+ def appears_later_in_flow?(route)
164
+ @coordinates.uuid_column(route.traverse_from) > route.column
165
+ end
166
+
148
167
  # Each Route object has a starting row. Each Route object has no knowledge
149
168
  # of other potential routes and pages/branches that may or may not exist in
150
169
  # them. The starting row may need to change dependent upon what has been
@@ -162,10 +181,14 @@ module MetadataPresenter
162
181
  end
163
182
 
164
183
  def add_by_coordinates
165
- @coordinates.each do |uuid, position|
184
+ service.flow.each_key do |uuid|
185
+ position = coordinates.position(uuid)
166
186
  next if detached?(position)
167
187
 
168
- @ordered[position[:column]][position[:row]] = get_flow_object(uuid)
188
+ column = position[:column]
189
+ row = position[:row]
190
+ insert_spacer(column, row) if occupied?(column, row, uuid)
191
+ @ordered[column][row] = get_flow_object(uuid)
169
192
  end
170
193
  end
171
194
 
@@ -173,6 +196,11 @@ module MetadataPresenter
173
196
  position[:row].nil? || position[:column].nil?
174
197
  end
175
198
 
199
+ def occupied?(column, row, uuid)
200
+ object = @ordered[column][row]
201
+ object.is_a?(MetadataPresenter::Flow) && object.uuid != uuid
202
+ end
203
+
176
204
  def get_flow_object(uuid)
177
205
  # main_flow is always empty if the Grid is _actually_ building the main flow
178
206
  return MetadataPresenter::Pointer.new(uuid: uuid) if main_flow.include?(uuid)
@@ -202,12 +230,12 @@ module MetadataPresenter
202
230
  # Find the very last MetadataPresenter::Flow object in every column and
203
231
  # remove any Spacer objects after that.
204
232
  def trim_spacers
205
- @ordered.each_with_index do |column, index|
233
+ @ordered.each_with_index do |column, column_number|
206
234
  last_index_of = column.rindex { |item| !item.is_a?(MetadataPresenter::Spacer) }
207
- trimmed_column = @ordered[index][0..last_index_of]
235
+ trimmed_column = @ordered[column_number][0..last_index_of]
208
236
 
209
237
  # We do not need any columns that only contain Spacer objects
210
- @ordered[index] = only_spacers?(trimmed_column) ? [] : trimmed_column
238
+ @ordered[column_number] = only_spacers?(trimmed_column) ? [] : trimmed_column
211
239
  end
212
240
  end
213
241
 
@@ -220,37 +248,25 @@ module MetadataPresenter
220
248
  # the one the branch is located in.
221
249
  def insert_expression_spacers
222
250
  service.branches.each do |branch|
223
- position = @coordinates[branch.uuid]
224
- next if position[:row].nil? || position[:column].nil? # detached branch
225
-
226
- next_column = position[:column] + 1
227
- uuids = []
228
- exiting_destinations_from_branch(branch).each.with_index(position[:row]) do |uuid, row|
229
- if uuids.include?(uuid)
230
- @ordered[next_column].insert(row, MetadataPresenter::Spacer.new)
231
- end
251
+ next if coordinates.uuid_column(branch.uuid).nil?
232
252
 
233
- uuids.push(uuid) unless uuids.include?(uuid)
253
+ previous_uuid = ''
254
+ next_column = coordinates.uuid_column(branch.uuid) + 1
255
+ exiting_destinations_from_branch(branch).each_with_index do |uuid, row|
256
+ insert_spacer(next_column, row) if uuid == previous_uuid
257
+ previous_uuid = uuid
234
258
  end
235
259
  end
236
260
  end
237
261
 
238
- # The frontend requires that expressions of type 'or' get there own line and
239
- # arrow. 'and' expression types continue to be grouped together.
240
- # Return the UUIDs of the destinations exiting a branch and allow duplicates
241
- # if the expression type is an 'or'.
242
- def exiting_destinations_from_branch(branch)
243
- destination_uuids = branch.conditionals.map do |conditional|
244
- if conditional.type == 'or'
245
- conditional.expressions.map(&:next)
246
- else
247
- conditional.next
248
- end
249
- end
250
- destination_uuids.flatten
262
+ def insert_spacer(column, row)
263
+ @ordered[column].insert(row, MetadataPresenter::Spacer.new)
251
264
  end
252
265
 
253
266
  # Any destinations exiting the branch that have not already been traversed.
267
+ # This removes any branch destinations that already exist on other rows. If
268
+ # that is the case then the arrow will flow towards whatever row that object
269
+ # is located.
254
270
  def routes_exiting_branch(branch)
255
271
  branch.all_destination_uuids.reject { |uuid| @traversed.include?(uuid) }
256
272
  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)