metadata_presenter 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3e99fe731812a98c2a9d2f6cefcade21ccd233f24118cd23440aa4d726b49c1
4
- data.tar.gz: 98496d517a2cdbf8da121d9d15469c9f017065921ca81b8133d895cec9bc9cff
3
+ metadata.gz: 5bd3891b62938a67dd025d49a96098778ad66221bf62cb2351002906e78aa4b4
4
+ data.tar.gz: 9dba99d36da50beea7a209d27f3371e4e814776d86d609436ac500c6d91cbd14
5
5
  SHA512:
6
- metadata.gz: f3735753a806e4b199b6420ed46469a06a856fa2140a3c893f91e187a8ae2a8068f5d54118ddefddabfd72db15ca04b9d532e9eb63019aa5d5611497ce322aee
7
- data.tar.gz: 5322246015561fc511b8fb1dc2072fec440df0fce1ca34555a3893b6a43b281b59d896a0941e3d69ea0f0114ea90408fc702b2bcd7a8080ade9934dc09bdd3ad
6
+ metadata.gz: c8c2dde3ca3e374e0f50eebc881a64fe676f4d6fa0156ab73e26e79e0c9a19292c4c21d55665171441c52af3c56a6472783365df050246ba7e583da89db112ce
7
+ data.tar.gz: c114dddbf37af9e800c9eb6c4601d6f250175efd1b35f9159a728edb0c3239357f6f35b281bd1280792101d669885e6f48218ffdeca876edbfbf5af5f0f9051e
@@ -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
+ @ordered = make_grid
18
+ add_columns
19
+ add_rows
20
+ add_by_coordinates
21
+ trim_spacers
22
+ insert_expression_spacers
23
+
24
+ @ordered
25
+ end
26
+
27
+ def ordered_flow
28
+ @ordered_flow ||= begin
29
+ flow = @ordered.empty? ? build.flatten : @ordered.flatten
30
+ flow.reject { |obj| obj.is_a?(MetadataPresenter::Spacer) }
31
+ end
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
@@ -1,3 +1,3 @@
1
1
  module MetadataPresenter
2
- VERSION = '2.4.2'.freeze
2
+ VERSION = '2.5.0'.freeze
3
3
  end
@@ -2,3 +2,4 @@ require 'metadata_presenter/engine'
2
2
  require 'govuk_design_system_formbuilder'
3
3
  require 'json-schema'
4
4
  require 'kramdown'
5
+ require 'active_support/notifications'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metadata_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.2
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - MoJ Forms
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-27 00:00:00.000000000 Z
11
+ date: 2021-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_design_system_formbuilder
@@ -283,6 +283,7 @@ files:
283
283
  - app/models/metadata_presenter/expression.rb
284
284
  - app/models/metadata_presenter/file_uploader.rb
285
285
  - app/models/metadata_presenter/flow.rb
286
+ - app/models/metadata_presenter/grid.rb
286
287
  - app/models/metadata_presenter/item.rb
287
288
  - app/models/metadata_presenter/meta.rb
288
289
  - app/models/metadata_presenter/meta_item.rb
@@ -292,6 +293,7 @@ files:
292
293
  - app/models/metadata_presenter/page.rb
293
294
  - app/models/metadata_presenter/page_answers.rb
294
295
  - app/models/metadata_presenter/previous_page.rb
296
+ - app/models/metadata_presenter/route.rb
295
297
  - app/models/metadata_presenter/service.rb
296
298
  - app/models/metadata_presenter/traversed_pages.rb
297
299
  - app/models/metadata_presenter/uploaded_file.rb