Almirah 0.4.2 → 0.4.3
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/bin/almirah +2 -2
- data/lib/almirah/doc_fabric.rb +7 -0
- data/lib/almirah/doc_items/blockquote.rb +15 -16
- data/lib/almirah/doc_items/code_block.rb +19 -21
- data/lib/almirah/doc_items/controlled_paragraph.rb +2 -2
- data/lib/almirah/doc_items/controlled_table.rb +11 -11
- data/lib/almirah/doc_items/controlled_table_row.rb +15 -18
- data/lib/almirah/doc_items/doc_footer.rb +10 -13
- data/lib/almirah/doc_items/doc_item.rb +2 -2
- data/lib/almirah/doc_items/heading.rb +1 -1
- data/lib/almirah/doc_items/image.rb +25 -27
- data/lib/almirah/doc_items/markdown_list.rb +2 -2
- data/lib/almirah/doc_items/markdown_table.rb +9 -2
- data/lib/almirah/doc_items/scope_table.rb +188 -0
- data/lib/almirah/doc_items/text_line.rb +26 -22
- data/lib/almirah/doc_items/todo_block.rb +15 -16
- data/lib/almirah/doc_items/work_item.rb +129 -0
- data/lib/almirah/doc_parser.rb +14 -8
- data/lib/almirah/doc_types/base_document.rb +21 -5
- data/lib/almirah/doc_types/coverage.rb +3 -3
- data/lib/almirah/doc_types/critical_chain_page.rb +218 -0
- data/lib/almirah/doc_types/decision.rb +152 -7
- data/lib/almirah/doc_types/decision_grouping.rb +17 -0
- data/lib/almirah/doc_types/decisions_overview.rb +591 -31
- data/lib/almirah/doc_types/implementation.rb +98 -98
- data/lib/almirah/doc_types/index.rb +11 -12
- data/lib/almirah/doc_types/persistent_document.rb +1 -1
- data/lib/almirah/doc_types/planning_dates.rb +17 -0
- data/lib/almirah/doc_types/protocol.rb +16 -20
- data/lib/almirah/doc_types/source_file.rb +1 -1
- data/lib/almirah/doc_types/traceability.rb +124 -133
- data/lib/almirah/dom/doc_section.rb +1 -1
- data/lib/almirah/navigation_pane.rb +9 -13
- data/lib/almirah/project/critical_chain.rb +117 -0
- data/lib/almirah/project/doc_linker.rb +4 -4
- data/lib/almirah/project/fever_chart.rb +94 -0
- data/lib/almirah/project/project_data.rb +8 -2
- data/lib/almirah/project/work_item_scheduler.rb +166 -0
- data/lib/almirah/project/working_calendar.rb +112 -0
- data/lib/almirah/project.rb +146 -9
- data/lib/almirah/project_configuration.rb +126 -29
- data/lib/almirah/project_template.rb +6 -6
- data/lib/almirah/project_utility.rb +3 -5
- data/lib/almirah/search/specifications_db.rb +2 -2
- data/lib/almirah/source_file_parser.rb +2 -3
- data/lib/almirah/templates/css/main.css +160 -0
- data/lib/almirah/templates/scripts/main.js +3 -1
- data/lib/almirah.rb +1 -2
- metadata +11 -2
|
@@ -12,25 +12,25 @@ class TextLineToken
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
class ItalicToken < TextLineToken
|
|
15
|
-
def initialize
|
|
15
|
+
def initialize
|
|
16
16
|
@value = '*'
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
class BoldToken < TextLineToken
|
|
21
|
-
def initialize
|
|
21
|
+
def initialize
|
|
22
22
|
@value = '**'
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
class BoldAndItalicToken < TextLineToken
|
|
27
|
-
def initialize
|
|
27
|
+
def initialize
|
|
28
28
|
@value = '***'
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
class ParentheseLeft < TextLineToken
|
|
33
|
-
def initialize
|
|
33
|
+
def initialize
|
|
34
34
|
@value = '('
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -42,7 +42,7 @@ class ParentheseRight < TextLineToken
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
class SquareBracketLeft < TextLineToken
|
|
45
|
-
def initialize
|
|
45
|
+
def initialize
|
|
46
46
|
@value = '['
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -54,13 +54,13 @@ class SquareBracketRight < TextLineToken
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
class DoubleSquareBracketLeft < TextLineToken
|
|
57
|
-
def initialize
|
|
57
|
+
def initialize
|
|
58
58
|
@value = '[['
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
class DoubleSquareBracketRight < TextLineToken
|
|
63
|
-
def initialize
|
|
63
|
+
def initialize
|
|
64
64
|
@value = ']]'
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -72,13 +72,13 @@ class SquareBracketRightAndParentheseLeft < TextLineToken
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
class BacktickToken < TextLineToken
|
|
75
|
-
def initialize
|
|
75
|
+
def initialize
|
|
76
76
|
@value = '`'
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
class InlineCodeToken < TextLineToken
|
|
81
|
-
def initialize(raw)
|
|
81
|
+
def initialize(raw)
|
|
82
82
|
@value = raw
|
|
83
83
|
end
|
|
84
84
|
end
|
|
@@ -86,7 +86,7 @@ end
|
|
|
86
86
|
class TextLineParser
|
|
87
87
|
attr_accessor :supported_tokens
|
|
88
88
|
|
|
89
|
-
def initialize
|
|
89
|
+
def initialize
|
|
90
90
|
@supported_tokens = []
|
|
91
91
|
@supported_tokens.append(BoldAndItalicToken.new)
|
|
92
92
|
@supported_tokens.append(BoldToken.new)
|
|
@@ -102,7 +102,7 @@ class TextLineParser
|
|
|
102
102
|
@supported_tokens.append(TextLineToken.new)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
def tokenize(str)
|
|
105
|
+
def tokenize(str)
|
|
106
106
|
result = []
|
|
107
107
|
sl = str.length
|
|
108
108
|
si = 0
|
|
@@ -134,7 +134,7 @@ class TextLineParser
|
|
|
134
134
|
|
|
135
135
|
private
|
|
136
136
|
|
|
137
|
-
def fuse_backticks(tokens)
|
|
137
|
+
def fuse_backticks(tokens)
|
|
138
138
|
result = []
|
|
139
139
|
i = 0
|
|
140
140
|
while i < tokens.length
|
|
@@ -256,7 +256,7 @@ class TextLineBuilder
|
|
|
256
256
|
@builder_context = builder_context
|
|
257
257
|
end
|
|
258
258
|
|
|
259
|
-
def restore(token_list)
|
|
259
|
+
def restore(token_list)
|
|
260
260
|
result = ''
|
|
261
261
|
return '' if token_list.nil?
|
|
262
262
|
|
|
@@ -397,12 +397,12 @@ end
|
|
|
397
397
|
class TextLine < TextLineBuilderContext
|
|
398
398
|
include HtmlSafe
|
|
399
399
|
|
|
400
|
-
@@link_registry = nil
|
|
401
|
-
@@broken_links = []
|
|
400
|
+
@@link_registry = nil
|
|
401
|
+
@@broken_links = []
|
|
402
402
|
|
|
403
403
|
class << self
|
|
404
404
|
def link_registry=(registry)
|
|
405
|
-
@@link_registry = registry
|
|
405
|
+
@@link_registry = registry
|
|
406
406
|
end
|
|
407
407
|
|
|
408
408
|
def link_registry
|
|
@@ -416,7 +416,7 @@ class TextLine < TextLineBuilderContext
|
|
|
416
416
|
end
|
|
417
417
|
|
|
418
418
|
def reset_broken_links
|
|
419
|
-
@@broken_links = []
|
|
419
|
+
@@broken_links = []
|
|
420
420
|
end
|
|
421
421
|
|
|
422
422
|
def record_broken_link(document, target)
|
|
@@ -457,7 +457,7 @@ class TextLine < TextLineBuilderContext
|
|
|
457
457
|
"<code class=\"inline\">#{CGI.escapeHTML(str)}</code>"
|
|
458
458
|
end
|
|
459
459
|
|
|
460
|
-
def link(link_text, link_url)
|
|
460
|
+
def link(link_text, link_url)
|
|
461
461
|
raw = link_url.to_s
|
|
462
462
|
kind, target, fragment = classify_markdown_link(raw)
|
|
463
463
|
case kind
|
|
@@ -477,7 +477,7 @@ class TextLine < TextLineBuilderContext
|
|
|
477
477
|
|
|
478
478
|
# Resolves an Obsidian/wiki link "[[target#fragment|alias]]" to a managed
|
|
479
479
|
# document by its unique id/filename, independent of folder (ADR-186).
|
|
480
|
-
def wiki_link(inner)
|
|
480
|
+
def wiki_link(inner)
|
|
481
481
|
link_part, sep, alias_text = inner.partition('|')
|
|
482
482
|
target, _hash, fragment = link_part.partition('#')
|
|
483
483
|
display = (sep.empty? ? link_part : alias_text).strip
|
|
@@ -501,7 +501,7 @@ class TextLine < TextLineBuilderContext
|
|
|
501
501
|
# Classifies a Markdown link target as :internal (a managed document, resolved
|
|
502
502
|
# against the owning document's source directory), :broken (a local .md path
|
|
503
503
|
# that does not resolve), or :external (everything else). ADR-186.
|
|
504
|
-
def classify_markdown_link(raw)
|
|
504
|
+
def classify_markdown_link(raw)
|
|
505
505
|
path_part, _sep, fragment = raw.partition('#')
|
|
506
506
|
path_part = URI::DEFAULT_PARSER.unescape(path_part) # decode %20 etc. to match the real file path
|
|
507
507
|
return [:external] unless local_markdown_path?(path_part)
|
|
@@ -510,10 +510,14 @@ class TextLine < TextLineBuilderContext
|
|
|
510
510
|
return [:external] unless doc&.output_rel_path && doc.respond_to?(:path) && doc.path && TextLine.link_registry
|
|
511
511
|
|
|
512
512
|
target = TextLine.link_registry.find_by_source(File.expand_path(path_part, File.dirname(doc.path)))
|
|
513
|
-
|
|
513
|
+
if target
|
|
514
|
+
[:internal, target, fragment.empty? ? nil : fragment]
|
|
515
|
+
else
|
|
516
|
+
[:broken]
|
|
517
|
+
end
|
|
514
518
|
end
|
|
515
519
|
|
|
516
520
|
def local_markdown_path?(path_part)
|
|
517
|
-
path_part.match?(/\.(md|markdown)\z/i) && !path_part.match?(%r{\A[a-z][a-z0-9
|
|
521
|
+
path_part.match?(/\.(md|markdown)\z/i) && !path_part.match?(%r{\A[a-z][a-z0-9+.-]*://}i)
|
|
518
522
|
end
|
|
519
523
|
end
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative 'doc_item'
|
|
2
2
|
|
|
3
3
|
class TodoBlock < DocItem
|
|
4
|
+
attr_accessor :text
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
def initialize(text)
|
|
7
|
+
@text = text
|
|
8
|
+
end
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
def to_html
|
|
11
|
+
s = ''
|
|
12
|
+
f_text = format_string(@text)
|
|
13
|
+
if @@html_table_render_in_progress
|
|
14
|
+
s += "</table>\n"
|
|
15
|
+
@@html_table_render_in_progress = false
|
|
9
16
|
end
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
s += "</table>\n"
|
|
16
|
-
@@html_table_render_in_progress = false
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
s += "<div class=\"todoblock\"><p>#{f_text}</div>\n"
|
|
20
|
-
return s
|
|
21
|
-
end
|
|
22
|
-
end
|
|
18
|
+
s += "<div class=\"todoblock\"><p>#{f_text}</div>\n"
|
|
19
|
+
s
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A single Scope-table row of a Decision Record, modelled as a node in the
|
|
4
|
+
# per-row dependency network (ADR-194). Each work item carries its canonical
|
|
5
|
+
# identity (`<record>.<step>.<activity>`), the bounded per-row Status it reads
|
|
6
|
+
# its readiness from (ADR-193), and predecessor / successor edges spanning both
|
|
7
|
+
# intra-record (step order) and cross-record (Depends On) links.
|
|
8
|
+
#
|
|
9
|
+
# `predecessors` / `successors` follow the house list-of-single-key-hashes shape
|
|
10
|
+
# (ADR-197): each entry maps a readable `record.step.activity` label to the
|
|
11
|
+
# resolved WorkItem. Every cross-record edge additionally records whether it
|
|
12
|
+
# stays within a decision_groups folder (in-group) or crosses to another
|
|
13
|
+
# (cross-group); that tag is inert here and consumed by the critical-chain step
|
|
14
|
+
# (ADR-195). Readiness ignores it — a cross-group predecessor blocks exactly
|
|
15
|
+
# like an in-group one.
|
|
16
|
+
class WorkItem
|
|
17
|
+
# Canonical phase order used only as the fallback for activity-type-aligned
|
|
18
|
+
# resolution when the prerequisite has no row of the dependent's exact Item.
|
|
19
|
+
ACTIVITY_ORDER = %w[Analysis Requirements Code Tests].freeze
|
|
20
|
+
|
|
21
|
+
attr_reader :record_id, :step, :activity, :owner, :status, :depends_on_refs,
|
|
22
|
+
:focused_estimate, :safe_estimate, :record_sequence, :start_date, :target_date
|
|
23
|
+
attr_accessor :predecessors, :successors, :cross_group_predecessor_labels, :resolved_dependencies
|
|
24
|
+
|
|
25
|
+
def initialize(record_id:, step:, activity:, owner:, status:, depends_on_refs:, # rubocop:disable Metrics/ParameterLists
|
|
26
|
+
focused_estimate: 0.0, safe_estimate: 0.0, record_sequence: 0,
|
|
27
|
+
start_date: nil, target_date: nil)
|
|
28
|
+
@record_id = record_id
|
|
29
|
+
@step = step
|
|
30
|
+
@activity = activity.to_s.strip
|
|
31
|
+
@owner = owner.to_s.strip
|
|
32
|
+
@status = status.to_s.strip
|
|
33
|
+
@depends_on_refs = depends_on_refs # array of record ref strings, e.g. ["ADR-193"]
|
|
34
|
+
# The row's authored committed window (ADR-213): the Scope Start Date and
|
|
35
|
+
# Target Date as parsed Dates (nil when absent/unparseable). Distinct from the
|
|
36
|
+
# computed schedule -- these are the author's intent, drawn on the tracking lane.
|
|
37
|
+
@start_date = start_date
|
|
38
|
+
@target_date = target_date
|
|
39
|
+
# Per-row scheduling estimates in working days (ADR-195); the focused estimate
|
|
40
|
+
# is the scheduling duration, safe - focused the safety aggregated into the
|
|
41
|
+
# project buffer. record_sequence is the owning record's number, used only as
|
|
42
|
+
# a deterministic priority tiebreak in the critical-chain scheduler.
|
|
43
|
+
@focused_estimate = focused_estimate
|
|
44
|
+
@safe_estimate = safe_estimate
|
|
45
|
+
@record_sequence = record_sequence
|
|
46
|
+
@predecessors = []
|
|
47
|
+
@successors = []
|
|
48
|
+
@cross_group_predecessor_labels = []
|
|
49
|
+
# One entry per resolved Depends On reference, keyed by the authored ref:
|
|
50
|
+
# { ref => { doc:, anchor:, label: } }. `anchor` is the target's resolved
|
|
51
|
+
# work-item row anchor (nil when the target has no `#` step column, so the
|
|
52
|
+
# link opens the record page); `label` is the resolved work item's id, for
|
|
53
|
+
# the link tooltip. Used to render the Depends On cell as deep links.
|
|
54
|
+
@resolved_dependencies = {}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def id
|
|
58
|
+
"#{@record_id}.#{@step}.#{@activity}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The anchor of this work item's row in its rendered Scope table — the deep-link
|
|
62
|
+
# target a dependent record points at. Namespaced with `.scope.` so it never
|
|
63
|
+
# collides with the Affected Documents controlled table, whose rows anchor on
|
|
64
|
+
# the same `<record>.<step>` scheme on the same page.
|
|
65
|
+
def row_anchor
|
|
66
|
+
"#{@record_id}.scope.#{@step}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add_resolved_dependency(ref, doc, anchor, label)
|
|
70
|
+
@resolved_dependencies[ref] = { doc: doc, anchor: anchor, label: label }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def activity_rank
|
|
74
|
+
ACTIVITY_ORDER.index(@activity) || ACTIVITY_ORDER.length
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# A row is "started" once it leaves To Do; both In-Progress and Done count, so
|
|
78
|
+
# a Done row whose predecessor never finished is still flagged as a violation.
|
|
79
|
+
def started?
|
|
80
|
+
@status == 'In-Progress' || @status == 'Done'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def done?
|
|
84
|
+
@status == 'Done'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def add_predecessor(work_item, cross_group:)
|
|
88
|
+
@predecessors << { work_item.id => work_item }
|
|
89
|
+
@cross_group_predecessor_labels << work_item.id if cross_group
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def add_successor(work_item)
|
|
93
|
+
@successors << { work_item.id => work_item }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def predecessor_items
|
|
97
|
+
@predecessors.map { |edge| edge.values.first }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def successor_items
|
|
101
|
+
@successors.map { |edge| edge.values.first }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Same-record, lower-numbered steps — the intra-record (phase-order) edges.
|
|
105
|
+
def intra_record_predecessors
|
|
106
|
+
predecessor_items.select { |p| p.record_id == @record_id }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Resolved Depends On edges into other records (in-group or cross-group alike).
|
|
110
|
+
def cross_record_predecessors
|
|
111
|
+
predecessor_items.reject { |p| p.record_id == @record_id }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Kitted when every predecessor — intra- and cross-record, regardless of the
|
|
115
|
+
# in-group / cross-group tag — is Done; trivially kitted when it has none.
|
|
116
|
+
def fully_kitted?
|
|
117
|
+
predecessor_items.all?(&:done?)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# A started row with an unfinished lower-numbered step in the same record.
|
|
121
|
+
def phase_order_violation?
|
|
122
|
+
started? && intra_record_predecessors.any? { |p| !p.done? }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# A started row whose resolved cross-record predecessor is not yet Done.
|
|
126
|
+
def cross_record_violation?
|
|
127
|
+
started? && cross_record_predecessors.any? { |p| !p.done? }
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/almirah/doc_parser.rb
CHANGED
|
@@ -8,13 +8,14 @@ require_relative 'doc_items/todo_block'
|
|
|
8
8
|
require_relative 'doc_items/controlled_paragraph'
|
|
9
9
|
require_relative 'doc_items/markdown_table'
|
|
10
10
|
require_relative 'doc_items/controlled_table'
|
|
11
|
+
require_relative 'doc_items/scope_table'
|
|
11
12
|
require_relative 'doc_items/image'
|
|
12
13
|
require_relative 'doc_items/markdown_list'
|
|
13
14
|
require_relative 'doc_items/doc_footer'
|
|
14
15
|
require_relative 'doc_items/frontmatter'
|
|
15
16
|
|
|
16
|
-
class DocParser
|
|
17
|
-
def self.try_to_extract_frontmatter(doc, text_lines)
|
|
17
|
+
class DocParser
|
|
18
|
+
def self.try_to_extract_frontmatter(doc, text_lines)
|
|
18
19
|
lines_to_remove = 0
|
|
19
20
|
frontmatter_lines = ''
|
|
20
21
|
if /^(-{3,})/.match(text_lines[0])
|
|
@@ -174,7 +175,7 @@ class DocParser # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
174
175
|
|
|
175
176
|
doc.items.append(item)
|
|
176
177
|
|
|
177
|
-
elsif res = /^([
|
|
178
|
+
elsif res = /^([*-]\s+)(.*)/.match(s) # check if unordered list start
|
|
178
179
|
|
|
179
180
|
if doc.title == ''
|
|
180
181
|
# dummy section if root is not a Document Title (level 0)
|
|
@@ -187,7 +188,7 @@ class DocParser # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
187
188
|
|
|
188
189
|
temp_md_table = process_temp_table(doc, temp_md_table)
|
|
189
190
|
|
|
190
|
-
|
|
191
|
+
res[2]
|
|
191
192
|
|
|
192
193
|
if temp_md_list
|
|
193
194
|
temp_md_list.add_row(s)
|
|
@@ -201,7 +202,7 @@ class DocParser # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
201
202
|
|
|
202
203
|
temp_md_table = process_temp_table(doc, temp_md_table)
|
|
203
204
|
|
|
204
|
-
|
|
205
|
+
res[1]
|
|
205
206
|
|
|
206
207
|
if temp_md_list
|
|
207
208
|
temp_md_list.add_row(s)
|
|
@@ -248,9 +249,14 @@ class DocParser # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
248
249
|
|
|
249
250
|
if temp_md_table
|
|
250
251
|
if temp_md_table.is_separator_detected # if there is a separator
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
# A Decision Record's Scope table is parsed into a purpose-built
|
|
253
|
+
# ScopeTable (ADR-194); a Protocol, or a Decision inside an
|
|
254
|
+
# "Affected Documents" section, into a ControlledTable.
|
|
255
|
+
if doc.instance_of?(Decision) && in_section?(doc, 'Scope') &&
|
|
256
|
+
temp_md_table.instance_of?(MarkdownTable)
|
|
257
|
+
temp_md_table = ScopeTable.new(doc, temp_md_table)
|
|
258
|
+
elsif doc.instance_of?(Protocol) ||
|
|
259
|
+
(doc.instance_of?(Decision) && in_section?(doc, 'Affected Documents'))
|
|
254
260
|
# check if it is a controlled table
|
|
255
261
|
tmp = /(.*)\s+>\[(\S*)\]/.match(row)
|
|
256
262
|
if tmp && (temp_md_table.instance_of? MarkdownTable)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../relative_url'
|
|
4
4
|
|
|
5
|
-
class BaseDocument
|
|
5
|
+
class BaseDocument
|
|
6
6
|
attr_accessor :title, :id, :dom, :headings, :output_rel_path
|
|
7
7
|
|
|
8
8
|
class << self
|
|
@@ -17,7 +17,13 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
17
17
|
@dom = nil
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Whether this page needs the Chart.js library loaded (overridden by the
|
|
21
|
+
# planning pages that render charts).
|
|
22
|
+
def needs_chartjs?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def save_html_to_file(html_rows, nav_pane, output_file_path)
|
|
21
27
|
gem_root = File.expand_path './../../..', File.dirname(__FILE__)
|
|
22
28
|
template_file = "#{gem_root}/lib/almirah/templates/page.html"
|
|
23
29
|
|
|
@@ -29,6 +35,8 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
29
35
|
"#{@id}.html"
|
|
30
36
|
elsif instance_of? DecisionsOverview
|
|
31
37
|
'overview.html'
|
|
38
|
+
elsif instance_of? CriticalChainPage
|
|
39
|
+
'critical-chain.html'
|
|
32
40
|
elsif instance_of? Decision
|
|
33
41
|
"#{@id}.html"
|
|
34
42
|
else
|
|
@@ -36,7 +44,7 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
36
44
|
end
|
|
37
45
|
@output_rel_path = output_file_path.split('/build/', 2).last
|
|
38
46
|
file = File.open(output_file_path, 'w')
|
|
39
|
-
file_data.each do |s|
|
|
47
|
+
file_data.each do |s|
|
|
40
48
|
if s.include?('{{CONTENT}}')
|
|
41
49
|
html_rows.each do |r|
|
|
42
50
|
file.puts r
|
|
@@ -51,7 +59,7 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
51
59
|
if @id == 'index'
|
|
52
60
|
file.puts "<script type=\"module\" src=\"#{rel_to('scripts/orama_search.js')}\"></script>"
|
|
53
61
|
file.puts "<link rel=\"stylesheet\" href=\"#{rel_to('css/search.css')}\">"
|
|
54
|
-
elsif
|
|
62
|
+
elsif needs_chartjs?
|
|
55
63
|
file.puts '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>'
|
|
56
64
|
end
|
|
57
65
|
elsif s.include?('{{HOME_BUTTON}}')
|
|
@@ -60,7 +68,10 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
60
68
|
else
|
|
61
69
|
file.puts index_link(rel_to('index.html'))
|
|
62
70
|
end
|
|
63
|
-
|
|
71
|
+
if BaseDocument.show_decisions_link
|
|
72
|
+
file.puts decisions_link(rel_to('decisions/overview.html'))
|
|
73
|
+
file.puts critical_chain_link(rel_to('decisions/critical-chain.html'))
|
|
74
|
+
end
|
|
64
75
|
elsif s.include?('{{GEM_VERSION}}')
|
|
65
76
|
file.puts "(#{Gem.loaded_specs['Almirah'].version.version})"
|
|
66
77
|
else
|
|
@@ -75,6 +86,11 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
75
86
|
%(<a id="decisions_menu_item" href="#{href}">#{icon} Decision Records</a>)
|
|
76
87
|
end
|
|
77
88
|
|
|
89
|
+
def critical_chain_link(href)
|
|
90
|
+
icon = '<span><i class="fa fa-link" aria-hidden="true"></i></span>'
|
|
91
|
+
%(<a id="critical_chain_menu_item" href="#{href}">#{icon} Critical Chain</a>)
|
|
92
|
+
end
|
|
93
|
+
|
|
78
94
|
def index_link(href)
|
|
79
95
|
icon = '<span><i class="fa fa-info" aria-hidden="true"></i></span>'
|
|
80
96
|
%(<a id="index_menu_item" href="#{href}">#{icon} Index</a>)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'base_document'
|
|
4
4
|
|
|
5
|
-
class Coverage < BaseDocument
|
|
5
|
+
class Coverage < BaseDocument
|
|
6
6
|
attr_accessor :top_doc, :bottom_doc, :covered_items, :passed_steps_number, :failed_steps_number
|
|
7
7
|
|
|
8
8
|
def initialize(top_doc)
|
|
@@ -21,7 +21,7 @@ class Coverage < BaseDocument # rubocop:disable Style/Documentation
|
|
|
21
21
|
puts "\e[35mTraceability: #{@id}\e[0m"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def to_html(nav_pane, output_file_path)
|
|
24
|
+
def to_html(nav_pane, output_file_path)
|
|
25
25
|
html_rows = []
|
|
26
26
|
|
|
27
27
|
html_rows.append('')
|
|
@@ -43,7 +43,7 @@ class Coverage < BaseDocument # rubocop:disable Style/Documentation
|
|
|
43
43
|
save_html_to_file(html_rows, nav_pane, output_file_path)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def render_table_row(top_item)
|
|
46
|
+
def render_table_row(top_item)
|
|
47
47
|
s = ''
|
|
48
48
|
if top_item.coverage_links
|
|
49
49
|
id_color = if top_item.coverage_links.length > 1
|