Almirah 0.2.8 → 0.3.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 +4 -4
- data/bin/almirah +0 -0
- data/lib/almirah/doc_fabric.rb +27 -1
- data/lib/almirah/doc_items/controlled_paragraph.rb +5 -3
- data/lib/almirah/doc_items/paragraph.rb +1 -0
- data/lib/almirah/doc_items/source_code_paragraph.rb +7 -0
- data/lib/almirah/doc_types/base_document.rb +11 -0
- data/lib/almirah/doc_types/implementation.rb +104 -0
- data/lib/almirah/doc_types/index.rb +39 -4
- data/lib/almirah/doc_types/source_file.rb +112 -0
- data/lib/almirah/doc_types/specification.rb +3 -1
- data/lib/almirah/project/doc_linker.rb +105 -0
- data/lib/almirah/project/project_data.rb +18 -0
- data/lib/almirah/project.rb +78 -61
- data/lib/almirah/project_configuration.rb +18 -18
- data/lib/almirah/source_file_parser.rb +91 -0
- data/lib/almirah/templates/page.html +1 -2
- data/lib/almirah/templates/scripts/main.js +0 -17
- metadata +29 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be9e792ca43617f09611a801b8e8c8efde741ec531a97295de8d65378232c0d2
|
|
4
|
+
data.tar.gz: 492000cb9aadaf0731e077b46ec3a32b573da9c8cd2acfaff6f282dec75c394d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51e65415ee9d0a8c0f77d19d21ab525a01cbc4c025bc5572f92f2df13bc56ceed8b04be5e18d3c0308c468c43986dd267046d9b1c125e281ad01476b08a93569
|
|
7
|
+
data.tar.gz: b64d9b5a0da5d254ecbc6dd861db05017b8cd81e13c0f58071413816d9b20d879187cbda5b2a703d5eae9f92ec044000010fc6b1b103fa30a9de04b265562cde
|
data/bin/almirah
CHANGED
|
File without changes
|
data/lib/almirah/doc_fabric.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
require_relative 'doc_types/base_document'
|
|
2
2
|
require_relative 'doc_types/specification'
|
|
3
|
+
require_relative 'doc_types/source_file'
|
|
3
4
|
require_relative 'doc_types/protocol'
|
|
4
5
|
require_relative 'doc_types/coverage'
|
|
6
|
+
require_relative 'doc_types/implementation'
|
|
7
|
+
require_relative 'doc_types/traceability'
|
|
5
8
|
require_relative 'doc_parser'
|
|
9
|
+
require_relative 'source_file_parser'
|
|
6
10
|
require_relative 'dom/document'
|
|
7
11
|
require_relative 'doc_items/heading'
|
|
8
12
|
|
|
@@ -51,8 +55,22 @@ class DocFabric
|
|
|
51
55
|
doc
|
|
52
56
|
end
|
|
53
57
|
|
|
54
|
-
def self.
|
|
58
|
+
def self.create_implementation_document(top_document)
|
|
59
|
+
doc = Implementation.new top_document
|
|
60
|
+
Heading.reset_global_section_number
|
|
61
|
+
doc.headings << Heading.new(doc, doc.title, 0)
|
|
62
|
+
# Build dom
|
|
63
|
+
doc.dom = Document.new(doc.headings)
|
|
64
|
+
doc
|
|
65
|
+
end
|
|
55
66
|
|
|
67
|
+
def self.create_source_file(repository_path, path, repository_name)
|
|
68
|
+
doc = SourceFile.new repository_path, path, repository_name
|
|
69
|
+
DocFabric.parse_source_file doc
|
|
70
|
+
doc
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.parse_document(doc)
|
|
56
74
|
file = File.open(doc.path)
|
|
57
75
|
file_lines = file.readlines
|
|
58
76
|
file.close
|
|
@@ -62,4 +80,12 @@ class DocFabric
|
|
|
62
80
|
# Build dom
|
|
63
81
|
doc.dom = Document.new(doc.headings) if doc.is_a?(Specification) || doc.is_a?(Protocol)
|
|
64
82
|
end
|
|
83
|
+
|
|
84
|
+
def self.parse_source_file(doc)
|
|
85
|
+
file = File.open(doc.path)
|
|
86
|
+
file_lines = file.readlines
|
|
87
|
+
file.close
|
|
88
|
+
|
|
89
|
+
SourceFileParser.parse(doc, file_lines)
|
|
90
|
+
end
|
|
65
91
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
require_relative 'paragraph'
|
|
2
2
|
|
|
3
|
+
# <REQ> Implementa a controlled paragraph as a subclass of the DocItem >[SRS-001] </REQ>
|
|
3
4
|
class ControlledParagraph < Paragraph
|
|
4
|
-
attr_accessor :id, :up_link_ids, :down_links, :coverage_links
|
|
5
|
+
attr_accessor :id, :up_link_ids, :down_links, :coverage_links, :source_code_links
|
|
5
6
|
|
|
6
7
|
def initialize(doc, text, id)
|
|
7
8
|
super(doc, text)
|
|
@@ -10,6 +11,7 @@ class ControlledParagraph < Paragraph
|
|
|
10
11
|
@up_link_ids = nil
|
|
11
12
|
@down_links = nil
|
|
12
13
|
@coverage_links = nil
|
|
14
|
+
@source_code_links = nil
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def to_html
|
|
@@ -32,7 +34,7 @@ class ControlledParagraph < Paragraph
|
|
|
32
34
|
up_link_doc_name = tmp[1].downcase
|
|
33
35
|
end
|
|
34
36
|
s += "\t\t<td class=\"item_id\">\
|
|
35
|
-
<a href=\"
|
|
37
|
+
<a href=\"#{parent_doc.specifications_path}#{up_link_doc_name}/#{up_link_doc_name}.html##{@up_link_ids[0]}\" \
|
|
36
38
|
class=\"external\" title=\"Linked to\">#{@up_link_ids[0]}</a></td>\n"
|
|
37
39
|
else
|
|
38
40
|
s += "\t\t<td class=\"item_id\">"
|
|
@@ -45,7 +47,7 @@ class ControlledParagraph < Paragraph
|
|
|
45
47
|
if tmp = /^([a-zA-Z]+)-\d+/.match(lnk)
|
|
46
48
|
up_link_doc_name = tmp[1].downcase
|
|
47
49
|
end
|
|
48
|
-
s += "\t\t\t<a href=\"
|
|
50
|
+
s += "\t\t\t<a href=\"#{parent_doc.specifications_path}#{up_link_doc_name}/#{up_link_doc_name}.html##{lnk}\" \
|
|
49
51
|
class=\"external\" title=\"Linked to\">#{lnk}</a>\n<br>"
|
|
50
52
|
end
|
|
51
53
|
s += '</div>'
|
|
@@ -49,10 +49,21 @@ class BaseDocument # rubocop:disable Style/Documentation
|
|
|
49
49
|
elsif instance_of? Coverage
|
|
50
50
|
file.puts '<link rel="stylesheet" href="../../css/main.css">'
|
|
51
51
|
file.puts '<script src="../../scripts/main.js"></script>'
|
|
52
|
+
elsif instance_of? Implementation
|
|
53
|
+
file.puts '<link rel="stylesheet" href="../../css/main.css">'
|
|
54
|
+
file.puts '<script src="../../scripts/main.js"></script>'
|
|
52
55
|
elsif instance_of? Protocol
|
|
53
56
|
file.puts '<link rel="stylesheet" href="../../../css/main.css">'
|
|
54
57
|
file.puts '<script src="../../../scripts/main.js"></script>'
|
|
55
58
|
end
|
|
59
|
+
elsif s.include?('{{HOME_BUTTON}}')
|
|
60
|
+
if @id == 'index'
|
|
61
|
+
file.puts '<a id="home_menu_item" href="./index.html"><span><i class="fa fa-home" aria-hidden="true"></i></span> Home</a>'
|
|
62
|
+
elsif instance_of? Protocol
|
|
63
|
+
file.puts '<a id="index_menu_item" href="./../../../index.html"><span><i class="fa fa-info" aria-hidden="true"></i></span> Index</a>'
|
|
64
|
+
else
|
|
65
|
+
file.puts '<a id="index_menu_item" href="./../../index.html"><span><i class="fa fa-info" aria-hidden="true"></i></span> Index</a>'
|
|
66
|
+
end
|
|
56
67
|
elsif s.include?('{{GEM_VERSION}}')
|
|
57
68
|
file.puts "(#{Gem.loaded_specs['Almirah'].version.version})"
|
|
58
69
|
else
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require_relative 'base_document'
|
|
2
|
+
|
|
3
|
+
class Implementation < BaseDocument
|
|
4
|
+
attr_accessor :top_doc, :bottom_doc, :items, :is_agregated, :traced_items
|
|
5
|
+
|
|
6
|
+
def initialize(top_doc)
|
|
7
|
+
super()
|
|
8
|
+
@top_doc = top_doc
|
|
9
|
+
|
|
10
|
+
@is_agregated = true
|
|
11
|
+
@traced_items = {}
|
|
12
|
+
|
|
13
|
+
@id = if @is_agregated
|
|
14
|
+
top_doc.id + '-sources'
|
|
15
|
+
else
|
|
16
|
+
top_doc.id + '-' + bottom_doc.id
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@title = 'Implementation Matrix: ' + @id
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_console
|
|
23
|
+
puts "\e[35m" + 'Implementation: ' + @id + "\e[0m"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_html(nav_pane, output_file_path)
|
|
27
|
+
html_rows = []
|
|
28
|
+
|
|
29
|
+
html_rows.append('')
|
|
30
|
+
s = "<h1>#{@title}</h1>\n"
|
|
31
|
+
s += "<table class=\"controlled\">\n"
|
|
32
|
+
s += "\t<thead>"
|
|
33
|
+
s += "\t\t<th>#</th>"
|
|
34
|
+
s += "\t\t<th style='font-weight: bold;'>#{@top_doc.title}</th>"
|
|
35
|
+
s += "\t\t<th>#</th>"
|
|
36
|
+
s += "\t\t<th style='font-weight: bold;'>Repository</th>"
|
|
37
|
+
s += "\t\t<th style='font-weight: bold;'>File Name</th>"
|
|
38
|
+
s += "\t\t<th style='font-weight: bold;'>Comment</th>"
|
|
39
|
+
s += "\t</thead>\n"
|
|
40
|
+
html_rows.append s
|
|
41
|
+
|
|
42
|
+
sorted_items = @top_doc.controlled_items.sort_by { |w| w.id }
|
|
43
|
+
|
|
44
|
+
sorted_items.each do |top_item|
|
|
45
|
+
row = render_table_row top_item
|
|
46
|
+
html_rows.append row
|
|
47
|
+
end
|
|
48
|
+
html_rows.append "</table>\n"
|
|
49
|
+
|
|
50
|
+
save_html_to_file(html_rows, nav_pane, output_file_path)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_table_row(top_item)
|
|
54
|
+
s = ''
|
|
55
|
+
top_f_text = top_item.format_string(top_item.text)
|
|
56
|
+
id_color = ''
|
|
57
|
+
|
|
58
|
+
if top_item.source_code_links && top_item.source_code_links.length.positive?
|
|
59
|
+
|
|
60
|
+
top_item_rendered = false
|
|
61
|
+
|
|
62
|
+
top_item.source_code_links.each do |bottom_item|
|
|
63
|
+
id_color = "style='background-color: #cff4d2;'"
|
|
64
|
+
bottom_f_text = bottom_item.format_string(bottom_item.text)
|
|
65
|
+
file_name = bottom_item.parent_doc.id
|
|
66
|
+
repository = bottom_item.parent_doc.repository
|
|
67
|
+
|
|
68
|
+
p = bottom_item.parent_doc.html_file_path.split('/build/source_files/').last
|
|
69
|
+
html_source_file_relative_path = "./../../source_files/#{p}"
|
|
70
|
+
|
|
71
|
+
s += "\t<tr>\n"
|
|
72
|
+
s += "\t\t<td class=\"item_id\"><a href=\"./../#{top_item.parent_doc.id}/#{top_item.parent_doc.id}.html##{top_item.id}\" class=\"external\">#{top_item.id}</a></td>\n"
|
|
73
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'>#{top_f_text}</td>\n"
|
|
74
|
+
s += "\t\t<td class=\"item_id\"><a href=\"#{html_source_file_relative_path}##{bottom_item.id}\" class=\"external\">#{bottom_item.id}</a></td>\n"
|
|
75
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'>#{repository}</td>\n"
|
|
76
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'>#{file_name}</td>\n"
|
|
77
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'>#{bottom_f_text}</td>\n"
|
|
78
|
+
s += "\t</tr>\n"
|
|
79
|
+
top_item_rendered = true
|
|
80
|
+
@traced_items[top_item.id.to_s.downcase] = top_item
|
|
81
|
+
end
|
|
82
|
+
unless top_item_rendered
|
|
83
|
+
s += "\t<tr>\n"
|
|
84
|
+
s += "\t\t<td class=\"item_id\"><a href=\"./../#{top_item.parent_doc.id}/#{top_item.parent_doc.id}.html##{top_item.id}\" class=\"external\">#{top_item.id}</a></td>\n"
|
|
85
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'>#{top_f_text}</td>\n"
|
|
86
|
+
s += "\t\t<td class=\"item_id\"></td>\n"
|
|
87
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'></td>\n"
|
|
88
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'></td>\n"
|
|
89
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'></td>\n"
|
|
90
|
+
s += "\t</tr>\n"
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
s += "\t<tr>\n"
|
|
94
|
+
s += "\t\t<td class=\"item_id\"><a href=\"./../#{top_item.parent_doc.id}/#{top_item.parent_doc.id}.html##{top_item.id}\" class=\"external\">#{top_item.id}</a></td>\n"
|
|
95
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'>#{top_f_text}</td>\n"
|
|
96
|
+
s += "\t\t<td class=\"item_id\"></td>\n"
|
|
97
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'></td>\n"
|
|
98
|
+
s += "\t\t<td class=\"item_text\" style='width: 16%;'></td>\n"
|
|
99
|
+
s += "\t\t<td class=\"item_text\" style='width: 28%;'></td>\n"
|
|
100
|
+
s += "\t</tr>\n"
|
|
101
|
+
end
|
|
102
|
+
s
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -36,9 +36,10 @@ class Index < BaseDocument # rubocop:disable Metrics/ClassLength,Style/Documenta
|
|
|
36
36
|
s += "\t\t<th title=\"Number of 'TODO:' blocks in document\">TODOs</th>\n"
|
|
37
37
|
s += "\t\t<th title=\"The last Controlled Paragraph sequence number (ID) used in the document\">Last Used<br>ID</th>\n"
|
|
38
38
|
s += "</thead>\n"
|
|
39
|
+
|
|
39
40
|
html_rows.append s
|
|
40
41
|
|
|
41
|
-
sorted_items = @project.specifications.sort_by(&:id)
|
|
42
|
+
sorted_items = @project.project_data.specifications.sort_by(&:id)
|
|
42
43
|
|
|
43
44
|
sorted_items.each do |doc| # rubocop:disable Metrics/BlockLength
|
|
44
45
|
s = "\t<tr>\n"
|
|
@@ -101,7 +102,7 @@ class Index < BaseDocument # rubocop:disable Metrics/ClassLength,Style/Documenta
|
|
|
101
102
|
s += "</thead>\n"
|
|
102
103
|
html_rows.append s
|
|
103
104
|
|
|
104
|
-
sorted_items = @project.traceability_matrices.sort_by(&:id)
|
|
105
|
+
sorted_items = @project.project_data.traceability_matrices.sort_by(&:id)
|
|
105
106
|
# buble-up design inputs
|
|
106
107
|
design_inputs = []
|
|
107
108
|
others = []
|
|
@@ -131,7 +132,7 @@ class Index < BaseDocument # rubocop:disable Metrics/ClassLength,Style/Documenta
|
|
|
131
132
|
html_rows.append "</table>\n"
|
|
132
133
|
|
|
133
134
|
# Coverage Matrices
|
|
134
|
-
unless @project.coverage_matrices.empty?
|
|
135
|
+
unless @project.project_data.coverage_matrices.empty?
|
|
135
136
|
s = "<h2>Coverage Matrices</h2>\n"
|
|
136
137
|
s += "<table class=\"controlled\">\n"
|
|
137
138
|
s += "\t<thead>\n"
|
|
@@ -142,7 +143,7 @@ class Index < BaseDocument # rubocop:disable Metrics/ClassLength,Style/Documenta
|
|
|
142
143
|
s += "</thead>\n"
|
|
143
144
|
html_rows.append s
|
|
144
145
|
|
|
145
|
-
sorted_items = @project.coverage_matrices.sort_by(&:id)
|
|
146
|
+
sorted_items = @project.project_data.coverage_matrices.sort_by(&:id)
|
|
146
147
|
|
|
147
148
|
sorted_items.each do |doc|
|
|
148
149
|
s = "\t<tr>\n"
|
|
@@ -168,6 +169,40 @@ class Index < BaseDocument # rubocop:disable Metrics/ClassLength,Style/Documenta
|
|
|
168
169
|
html_rows.append "</table>\n"
|
|
169
170
|
end
|
|
170
171
|
|
|
172
|
+
# Implementation Matrices
|
|
173
|
+
unless @project.project_data.implementation_matrices.empty?
|
|
174
|
+
s = "<h2>Implementation Matrices</h2>\n"
|
|
175
|
+
s += "<table class=\"controlled\">\n"
|
|
176
|
+
s += "\t<thead>\n"
|
|
177
|
+
s += "\t\t<th>Title</th>\n"
|
|
178
|
+
s += "\t\t<th title=\"The ratio of Controlled Paragraphs mentioned in source code files and total number of Controlled Paragraphs\">Status</th>\n"
|
|
179
|
+
s += "\t\t<th>Specification Implemented</th>\n"
|
|
180
|
+
s += "</thead>\n"
|
|
181
|
+
html_rows.append s
|
|
182
|
+
|
|
183
|
+
sorted_items = @project.project_data.implementation_matrices.sort_by(&:id)
|
|
184
|
+
|
|
185
|
+
sorted_items.each do |doc|
|
|
186
|
+
s = "\t<tr>\n"
|
|
187
|
+
|
|
188
|
+
implemented_items = 0
|
|
189
|
+
doc.top_doc.controlled_items.each do |i|
|
|
190
|
+
implemented_items += 1 if i.source_code_links && i.source_code_links.length.positive? # rubocop:disable Style/SafeNavigation
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
coverage = implemented_items.to_f / doc.top_doc.controlled_items.length * 100.0
|
|
194
|
+
|
|
195
|
+
s += "\t\t<td class=\"item_text\" style='padding: 5px;'><a href=\"./specifications/#{doc.id}/#{doc.id}.html\" class=\"external\">#{doc.title}</a></td>\n"
|
|
196
|
+
s += "\t\t<td class=\"item_id\" style='width: 7%;'>#{'%.2f' % coverage}%</td>\n" # rubocop:disable Style/FormatString
|
|
197
|
+
s += "\t\t<td class=\"item_text\" style='width: 25%; padding: 5px;'>\
|
|
198
|
+
<i class=\"fa fa-file-text-o\" style='background-color: ##{doc.top_doc.color};'> </i>\
|
|
199
|
+
#{doc.top_doc.title}</td>\n"
|
|
200
|
+
s += "</tr>\n"
|
|
201
|
+
html_rows.append s
|
|
202
|
+
end
|
|
203
|
+
html_rows.append "</table>\n"
|
|
204
|
+
end
|
|
205
|
+
|
|
171
206
|
save_html_to_file(html_rows, nil, output_file_path)
|
|
172
207
|
end
|
|
173
208
|
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'persistent_document'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'rouge'
|
|
6
|
+
|
|
7
|
+
class SourceFile < PersistentDocument
|
|
8
|
+
attr_accessor :root_path, :repository, :dictionary, :wrong_links_hash,
|
|
9
|
+
:items_with_uplinks_number, :html_file_path, :specifications_path
|
|
10
|
+
|
|
11
|
+
def initialize(repository_path, fele_path, repository_name)
|
|
12
|
+
super fele_path
|
|
13
|
+
@root_path = repository_path
|
|
14
|
+
@id = File.basename(fele_path).downcase
|
|
15
|
+
@repository = repository_name
|
|
16
|
+
@html_file_path = '' # available only afer rendering
|
|
17
|
+
|
|
18
|
+
@dictionary = {}
|
|
19
|
+
@wrong_links_hash = {}
|
|
20
|
+
|
|
21
|
+
@items_with_uplinks_number = 0
|
|
22
|
+
|
|
23
|
+
# Calculate the relative path depth to determine correct number of parent directory symbols
|
|
24
|
+
relative_path = @path.sub("#{@root_path}/", '')
|
|
25
|
+
depth = relative_path.count('/') + 1 # +1 for the repository folder
|
|
26
|
+
depth += 1 # for the source_files folder
|
|
27
|
+
@specifications_path = "./#{'../' * depth}specifications/"
|
|
28
|
+
puts @specifications_path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_console
|
|
32
|
+
puts "\e[32mSource File: [#{@repository}] #{@id}\e[0m"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_html(output_file_path)
|
|
36
|
+
html_rows = []
|
|
37
|
+
|
|
38
|
+
html_rows.append('')
|
|
39
|
+
|
|
40
|
+
@items.each do |item|
|
|
41
|
+
a = item.to_html
|
|
42
|
+
html_rows.append a
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# make some nice lexed html
|
|
46
|
+
source = File.read(@path.to_s)
|
|
47
|
+
# Detect lexer from file extension
|
|
48
|
+
# lexer = Rouge::Lexer.find_fancy(@path.to_s, source) || Rouge::Lexers::PlainText.new
|
|
49
|
+
lexer = Rouge::Lexer.guess_by_filename(@path.to_s) || Rouge::Lexers::PlainText.new
|
|
50
|
+
formatter = Rouge::Formatters::HTML.new
|
|
51
|
+
|
|
52
|
+
# Add Base16 theme CSS
|
|
53
|
+
theme_css = Rouge::Themes::Pastie.render(scope: '.highlight')
|
|
54
|
+
html_rows.append "<style>\n#{theme_css}\n</style>"
|
|
55
|
+
|
|
56
|
+
# Format the source code with syntax highlighting
|
|
57
|
+
formatted_html = formatter.format(lexer.lex(source))
|
|
58
|
+
|
|
59
|
+
# Add formatted code with highlighting styles
|
|
60
|
+
html_rows.append '<div class="highlight" style="background-color:#f6f7f8;"><pre>'
|
|
61
|
+
html_rows.append formatted_html
|
|
62
|
+
html_rows.append '</pre></div>'
|
|
63
|
+
|
|
64
|
+
save_to_file(html_rows, nil, output_file_path)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def save_to_file(html_rows, nav_pane, output_file_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
68
|
+
gem_root = File.expand_path './../../..', File.dirname(__FILE__)
|
|
69
|
+
template_file = "#{gem_root}/lib/almirah/templates/page.html"
|
|
70
|
+
|
|
71
|
+
file = File.open(template_file)
|
|
72
|
+
file_data = file.readlines
|
|
73
|
+
file.close
|
|
74
|
+
|
|
75
|
+
output_file_path += "#{@repository}/"
|
|
76
|
+
output_file_path += @path.sub("#{@root_path}/", '')
|
|
77
|
+
output_file_path += '.html'
|
|
78
|
+
@html_file_path = output_file_path
|
|
79
|
+
FileUtils.mkdir_p(File.dirname(output_file_path))
|
|
80
|
+
|
|
81
|
+
# Calculate the relative path depth to determine correct number of parent directory symbols
|
|
82
|
+
relative_path = @path.sub("#{@root_path}/", '')
|
|
83
|
+
depth = relative_path.count('/') + 1 # +1 for the repository folder
|
|
84
|
+
depth += 1 # for the source_files folder
|
|
85
|
+
css_path = "#{'../' * depth}css/main.css"
|
|
86
|
+
js_path = "#{'../' * depth}scripts/main.js"
|
|
87
|
+
index_path = "#{'../' * depth}index.html"
|
|
88
|
+
|
|
89
|
+
file = File.open(output_file_path, 'w')
|
|
90
|
+
file_data.each do |s|
|
|
91
|
+
if s.include?('{{CONTENT}}')
|
|
92
|
+
html_rows.each do |r|
|
|
93
|
+
file.puts r
|
|
94
|
+
end
|
|
95
|
+
elsif s.include?('{{NAV_PANE}}')
|
|
96
|
+
file.puts nav_pane.to_html if nav_pane
|
|
97
|
+
elsif s.include?('{{DOCUMENT_TITLE}}')
|
|
98
|
+
file.puts s.gsub! '{{DOCUMENT_TITLE}}', @title
|
|
99
|
+
elsif s.include?('{{STYLES_AND_SCRIPTS}}')
|
|
100
|
+
file.puts "<link rel=\"stylesheet\" href=\"#{css_path}\">"
|
|
101
|
+
file.puts "<script src=\"#{js_path}\"></script>"
|
|
102
|
+
elsif s.include?('{{HOME_BUTTON}}')
|
|
103
|
+
file.puts "<a id=\"index_menu_item\" href=\"#{index_path}\"><span><i class=\"fa fa-info\" aria-hidden=\"true\"></i></span> Index</a>"
|
|
104
|
+
elsif s.include?('{{GEM_VERSION}}')
|
|
105
|
+
file.puts "(#{Gem.loaded_specs['Almirah'].version.version})"
|
|
106
|
+
else
|
|
107
|
+
file.puts s
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
file.close
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -5,7 +5,7 @@ require_relative 'persistent_document'
|
|
|
5
5
|
class Specification < PersistentDocument
|
|
6
6
|
attr_accessor :dictionary, :todo_blocks, :wrong_links_hash, :items_with_uplinks_number, :items_with_downlinks_number,
|
|
7
7
|
:items_with_coverage_number, :duplicated_ids_number, :duplicates_list, :last_used_id,
|
|
8
|
-
:last_used_id_number, :color
|
|
8
|
+
:last_used_id_number, :color, :source_code_links, :specifications_path
|
|
9
9
|
|
|
10
10
|
def initialize(fele_path)
|
|
11
11
|
super
|
|
@@ -13,6 +13,7 @@ class Specification < PersistentDocument
|
|
|
13
13
|
@duplicates_list = []
|
|
14
14
|
@todo_blocks = []
|
|
15
15
|
@wrong_links_hash = {}
|
|
16
|
+
@source_code_links = []
|
|
16
17
|
|
|
17
18
|
@items_with_uplinks_number = 0
|
|
18
19
|
@items_with_downlinks_number = 0
|
|
@@ -24,6 +25,7 @@ class Specification < PersistentDocument
|
|
|
24
25
|
@color = 'bbb'
|
|
25
26
|
|
|
26
27
|
@id = File.basename(fele_path, File.extname(fele_path)).downcase
|
|
28
|
+
@specifications_path = './../'
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def to_console
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'project_data'
|
|
4
|
+
|
|
5
|
+
class DocLinker
|
|
6
|
+
def self.link_all_source_files(project_data)
|
|
7
|
+
result = false
|
|
8
|
+
project_data.source_files.each do |f|
|
|
9
|
+
project_data.specifications.each do |s|
|
|
10
|
+
next unless f.up_link_docs.key?(s.id.to_s)
|
|
11
|
+
|
|
12
|
+
link_source_file_to_spec(f, s)
|
|
13
|
+
project_data.implemented_specifications_dictionary[s.id.to_s] = s
|
|
14
|
+
result = true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
result
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.link_protocol_to_spec(protocol, specification) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
21
|
+
top_document = specification
|
|
22
|
+
bottom_document = protocol
|
|
23
|
+
|
|
24
|
+
bottom_document.controlled_items.each do |item|
|
|
25
|
+
next unless item.up_link_ids
|
|
26
|
+
|
|
27
|
+
item.up_link_ids.each do |up_lnk|
|
|
28
|
+
if top_document.dictionary.key?(up_lnk.to_s)
|
|
29
|
+
|
|
30
|
+
top_item = top_document.dictionary[up_lnk.to_s]
|
|
31
|
+
|
|
32
|
+
unless top_item.coverage_links
|
|
33
|
+
top_item.coverage_links = []
|
|
34
|
+
top_document.items_with_coverage_number += 1 # for statistics
|
|
35
|
+
end
|
|
36
|
+
top_item.coverage_links.append(item)
|
|
37
|
+
elsif tmp = /^([a-zA-Z]+)-\d+/.match(up_lnk)
|
|
38
|
+
# check if there is a non existing link with the right doc_id
|
|
39
|
+
if tmp[1].downcase == top_document.id.downcase
|
|
40
|
+
bottom_document.wrong_links_hash[up_lnk] = item
|
|
41
|
+
end # SRS
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.link_source_file_to_spec(source_file, specification) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
48
|
+
top_document = specification
|
|
49
|
+
bottom_document = source_file
|
|
50
|
+
|
|
51
|
+
bottom_document.controlled_items.each do |item|
|
|
52
|
+
next unless item.up_link_ids
|
|
53
|
+
|
|
54
|
+
item.up_link_ids.each do |up_lnk|
|
|
55
|
+
if top_document.dictionary.key?(up_lnk.to_s)
|
|
56
|
+
|
|
57
|
+
top_item = top_document.dictionary[up_lnk.to_s]
|
|
58
|
+
|
|
59
|
+
top_item.source_code_links = [] unless top_item.source_code_links
|
|
60
|
+
top_item.source_code_links.append(item)
|
|
61
|
+
elsif tmp = /^([a-zA-Z]+)-\d+/.match(up_lnk)
|
|
62
|
+
# check if there is a non existing link with the right doc_id
|
|
63
|
+
if tmp[1].downcase == top_document.id.downcase
|
|
64
|
+
bottom_document.wrong_links_hash[up_lnk] = item
|
|
65
|
+
end # SRS
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.link_two_specifications(doc_a, doc_b) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
72
|
+
if doc_b.up_link_docs.key?(doc_a.id.to_s)
|
|
73
|
+
top_document = doc_a
|
|
74
|
+
bottom_document = doc_b
|
|
75
|
+
elsif doc_a.up_link_docs.key?(doc_b.id.to_s)
|
|
76
|
+
top_document = doc_b
|
|
77
|
+
bottom_document = doc_a
|
|
78
|
+
else
|
|
79
|
+
return false # no links
|
|
80
|
+
end
|
|
81
|
+
# puts "Link: #{doc_a.id} - #{doc_b.id}"
|
|
82
|
+
bottom_document.controlled_items.each do |item|
|
|
83
|
+
next unless item.up_link_ids
|
|
84
|
+
|
|
85
|
+
item.up_link_ids.each do |up_lnk|
|
|
86
|
+
if top_document.dictionary.key?(up_lnk.to_s)
|
|
87
|
+
|
|
88
|
+
top_item = top_document.dictionary[up_lnk.to_s]
|
|
89
|
+
|
|
90
|
+
unless top_item.down_links
|
|
91
|
+
top_item.down_links = []
|
|
92
|
+
top_document.items_with_downlinks_number += 1 # for statistics
|
|
93
|
+
end
|
|
94
|
+
top_item.down_links.append(item)
|
|
95
|
+
elsif tmp = /^([a-zA-Z]+)-\d+/.match(up_lnk)
|
|
96
|
+
# check if there is a non existing link with the right doc_id
|
|
97
|
+
if tmp[1].downcase == top_document.id.downcase
|
|
98
|
+
bottom_document.wrong_links_hash[up_lnk] = item
|
|
99
|
+
end # SRS
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class ProjectData
|
|
2
|
+
attr_reader :specifications, :protocols, :traceability_matrices, :coverage_matrices, :source_files,
|
|
3
|
+
:specifications_dictionary, :covered_specifications_dictionary, :implemented_specifications_dictionary,
|
|
4
|
+
:implementation_matrices
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@specifications = []
|
|
8
|
+
@protocols = []
|
|
9
|
+
@traceability_matrices = []
|
|
10
|
+
@coverage_matrices = []
|
|
11
|
+
@source_files = []
|
|
12
|
+
@implementation_matrices = []
|
|
13
|
+
|
|
14
|
+
@specifications_dictionary = {}
|
|
15
|
+
@covered_specifications_dictionary = {}
|
|
16
|
+
@implemented_specifications_dictionary = {}
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/almirah/project.rb
CHANGED
|
@@ -6,19 +6,17 @@ require_relative 'navigation_pane'
|
|
|
6
6
|
require_relative 'doc_types/traceability'
|
|
7
7
|
require_relative 'doc_types/index'
|
|
8
8
|
require_relative 'search/specifications_db'
|
|
9
|
+
require_relative 'project/doc_linker'
|
|
10
|
+
require_relative 'project_configuration'
|
|
11
|
+
require_relative 'project/project_data'
|
|
9
12
|
|
|
10
13
|
class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
11
|
-
attr_accessor :
|
|
12
|
-
:index, :project, :configuration
|
|
14
|
+
attr_accessor :index, :project, :configuration, :project_data
|
|
13
15
|
|
|
14
16
|
def initialize(configuration)
|
|
15
17
|
@configuration = configuration
|
|
16
|
-
@
|
|
17
|
-
|
|
18
|
-
@traceability_matrices = []
|
|
19
|
-
@coverage_matrices = []
|
|
20
|
-
@specifications_dictionary = {}
|
|
21
|
-
@covered_specifications_dictionary = {}
|
|
18
|
+
@project_data = ProjectData.new
|
|
19
|
+
|
|
22
20
|
@index = nil
|
|
23
21
|
@project = self
|
|
24
22
|
FileUtils.remove_dir("#{@configuration.project_root_directory}/build", true)
|
|
@@ -42,14 +40,18 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
42
40
|
def specifications_and_protocols # rubocop:disable Metrics/MethodLength
|
|
43
41
|
parse_all_specifications
|
|
44
42
|
parse_all_protocols
|
|
43
|
+
parse_all_source_files
|
|
45
44
|
link_all_specifications
|
|
46
45
|
link_all_protocols
|
|
46
|
+
link_all_source_files
|
|
47
47
|
check_wrong_specification_referenced
|
|
48
48
|
create_index
|
|
49
|
-
render_all_specifications(@specifications)
|
|
50
|
-
render_all_specifications(@traceability_matrices)
|
|
51
|
-
render_all_specifications(@coverage_matrices)
|
|
49
|
+
render_all_specifications(@project_data.specifications)
|
|
50
|
+
render_all_specifications(@project_data.traceability_matrices)
|
|
51
|
+
render_all_specifications(@project_data.coverage_matrices)
|
|
52
52
|
render_all_protocols
|
|
53
|
+
render_all_source_files
|
|
54
|
+
render_all_specifications(@project_data.implementation_matrices) # intentionally after source file rendering
|
|
53
55
|
render_index
|
|
54
56
|
create_search_data
|
|
55
57
|
end
|
|
@@ -57,14 +59,18 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
57
59
|
def specifications_and_results(test_run) # rubocop:disable Metrics/MethodLength
|
|
58
60
|
parse_all_specifications
|
|
59
61
|
parse_test_run test_run
|
|
62
|
+
parse_all_source_files
|
|
60
63
|
link_all_specifications
|
|
61
64
|
link_all_protocols
|
|
65
|
+
link_all_source_files
|
|
62
66
|
check_wrong_specification_referenced
|
|
63
67
|
create_index
|
|
64
|
-
render_all_specifications(@specifications)
|
|
65
|
-
render_all_specifications(@traceability_matrices)
|
|
66
|
-
render_all_specifications(@coverage_matrices)
|
|
68
|
+
render_all_specifications(@project_data.specifications)
|
|
69
|
+
render_all_specifications(@project_data.traceability_matrices)
|
|
70
|
+
render_all_specifications(@project_data.coverage_matrices)
|
|
67
71
|
render_all_protocols
|
|
72
|
+
render_all_source_files
|
|
73
|
+
render_all_specifications(@project_data.implementation_matrices) # intentionally after source file rendering
|
|
68
74
|
render_index
|
|
69
75
|
create_search_data
|
|
70
76
|
end
|
|
@@ -78,8 +84,8 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
78
84
|
# parse documents in the second pass
|
|
79
85
|
Dir.glob("#{path}/specifications/**/*.md").each do |f| # rubocop:disable Style/CombinableLoops
|
|
80
86
|
doc = DocFabric.create_specification(f)
|
|
81
|
-
@specifications.append(doc)
|
|
82
|
-
@specifications_dictionary[doc.id.to_s.downcase] = doc
|
|
87
|
+
@project_data.specifications.append(doc)
|
|
88
|
+
@project_data.specifications_dictionary[doc.id.to_s.downcase] = doc
|
|
83
89
|
end
|
|
84
90
|
end
|
|
85
91
|
|
|
@@ -87,7 +93,24 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
87
93
|
path = @configuration.project_root_directory
|
|
88
94
|
Dir.glob("#{path}/tests/protocols/**/*.md").each do |f|
|
|
89
95
|
doc = DocFabric.create_protocol(f)
|
|
90
|
-
@protocols.append(doc)
|
|
96
|
+
@project_data.protocols.append(doc)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def parse_all_source_files
|
|
101
|
+
@configuration.get_repositories.each do |repos|
|
|
102
|
+
# puts "Processing repository: #{repos['name']}, #{repos['path']}"
|
|
103
|
+
next unless repos['path'] && Dir.exist?(repos['path'])
|
|
104
|
+
|
|
105
|
+
file_path = repos['path']
|
|
106
|
+
Dir.glob("#{repos['path']}/**/*.*").each do |f|
|
|
107
|
+
next unless File.file?(f) && f.end_with?('.c', '.cpp', '.h', '.hpp', '.py', '.java', '.rb', '.js', '.ts', '.go',
|
|
108
|
+
'.rs')
|
|
109
|
+
|
|
110
|
+
doc = DocFabric.create_source_file(file_path, f, repos['name'])
|
|
111
|
+
# puts "Source file: #{doc.id}"
|
|
112
|
+
@project_data.source_files.append(doc)
|
|
113
|
+
end
|
|
91
114
|
end
|
|
92
115
|
end
|
|
93
116
|
|
|
@@ -95,52 +118,62 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
95
118
|
path = @configuration.project_root_directory
|
|
96
119
|
Dir.glob("#{path}/tests/runs/#{test_run}/**/*.md").each do |f|
|
|
97
120
|
doc = DocFabric.create_protocol(f)
|
|
98
|
-
@protocols.append(doc)
|
|
121
|
+
@project_data.protocols.append(doc)
|
|
99
122
|
end
|
|
100
123
|
end
|
|
101
124
|
|
|
102
|
-
def link_all_specifications # rubocop:disable Metrics/MethodLength
|
|
103
|
-
comb_list = @specifications.combination(2)
|
|
125
|
+
def link_all_specifications # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
126
|
+
comb_list = @project_data.specifications.combination(2)
|
|
104
127
|
comb_list.each do |c|
|
|
105
128
|
link_two_specifications(c[0], c[1])
|
|
106
129
|
# puts "Link: #{c[0].id} - #{c[1].id}"
|
|
107
130
|
end
|
|
108
131
|
# separatelly create design inputs treceability
|
|
109
132
|
@configuration.get_design_inputs.each do |i|
|
|
110
|
-
next unless @specifications_dictionary.key? i.to_s.downcase
|
|
133
|
+
next unless @project_data.specifications_dictionary.key? i.to_s.downcase
|
|
111
134
|
|
|
112
|
-
document = @specifications_dictionary[i.to_s.downcase]
|
|
135
|
+
document = @project_data.specifications_dictionary[i.to_s.downcase]
|
|
113
136
|
if document
|
|
114
137
|
doc = DocFabric.create_traceability_document(document, nil)
|
|
115
|
-
@traceability_matrices.append doc
|
|
138
|
+
@project_data.traceability_matrices.append doc
|
|
116
139
|
end
|
|
117
140
|
end
|
|
118
141
|
end
|
|
119
142
|
|
|
120
143
|
def link_all_protocols # rubocop:disable Metrics/MethodLength
|
|
121
|
-
@protocols.each do |p|
|
|
122
|
-
@specifications.each do |s|
|
|
144
|
+
@project_data.protocols.each do |p|
|
|
145
|
+
@project_data.specifications.each do |s|
|
|
123
146
|
if p.up_link_docs.key?(s.id.to_s)
|
|
124
|
-
link_protocol_to_spec(p, s)
|
|
125
|
-
@covered_specifications_dictionary[s.id.to_s] = s
|
|
147
|
+
DocLinker.link_protocol_to_spec(p, s)
|
|
148
|
+
@project_data.covered_specifications_dictionary[s.id.to_s] = s
|
|
126
149
|
end
|
|
127
150
|
end
|
|
128
151
|
end
|
|
129
152
|
# create coverage documents
|
|
130
|
-
@covered_specifications_dictionary.each do |_key, value|
|
|
153
|
+
@project_data.covered_specifications_dictionary.each do |_key, value|
|
|
131
154
|
doc = DocFabric.create_coverage_matrix(value)
|
|
132
|
-
@coverage_matrices.append doc
|
|
155
|
+
@project_data.coverage_matrices.append doc
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def link_all_source_files
|
|
160
|
+
return unless DocLinker.link_all_source_files(@project_data)
|
|
161
|
+
|
|
162
|
+
# create implementation documents
|
|
163
|
+
@project_data.implemented_specifications_dictionary.each do |_key, value|
|
|
164
|
+
doc = DocFabric.create_implementation_document(value)
|
|
165
|
+
@project_data.implementation_matrices.append doc
|
|
133
166
|
end
|
|
134
167
|
end
|
|
135
168
|
|
|
136
169
|
def check_wrong_specification_referenced # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
137
170
|
available_specification_ids = {}
|
|
138
171
|
|
|
139
|
-
@specifications.each do |s|
|
|
172
|
+
@project_data.specifications.each do |s|
|
|
140
173
|
available_specification_ids[s.id.to_s.downcase] = s
|
|
141
174
|
end
|
|
142
175
|
|
|
143
|
-
@specifications.each do |s| # rubocop:disable Style/CombinableLoops
|
|
176
|
+
@project_data.specifications.each do |s| # rubocop:disable Style/CombinableLoops
|
|
144
177
|
s.up_link_docs.each do |key, _value|
|
|
145
178
|
next if available_specification_ids.key?(key)
|
|
146
179
|
|
|
@@ -197,34 +230,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
197
230
|
end
|
|
198
231
|
# create treceability document
|
|
199
232
|
doc = DocFabric.create_traceability_document(top_document, bottom_document)
|
|
200
|
-
@traceability_matrices.append doc
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def link_protocol_to_spec(protocol, specification) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
204
|
-
top_document = specification
|
|
205
|
-
bottom_document = protocol
|
|
206
|
-
|
|
207
|
-
bottom_document.controlled_items.each do |item|
|
|
208
|
-
next unless item.up_link_ids
|
|
209
|
-
|
|
210
|
-
item.up_link_ids.each do |up_lnk|
|
|
211
|
-
if top_document.dictionary.key?(up_lnk.to_s)
|
|
212
|
-
|
|
213
|
-
top_item = top_document.dictionary[up_lnk.to_s]
|
|
214
|
-
|
|
215
|
-
unless top_item.coverage_links
|
|
216
|
-
top_item.coverage_links = []
|
|
217
|
-
top_document.items_with_coverage_number += 1 # for statistics
|
|
218
|
-
end
|
|
219
|
-
top_item.coverage_links.append(item)
|
|
220
|
-
elsif tmp = /^([a-zA-Z]+)-\d+/.match(up_lnk)
|
|
221
|
-
# check if there is a non existing link with the right doc_id
|
|
222
|
-
if tmp[1].downcase == top_document.id.downcase
|
|
223
|
-
bottom_document.wrong_links_hash[up_lnk] = item
|
|
224
|
-
end # SRS
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
233
|
+
@project_data.traceability_matrices.append doc
|
|
228
234
|
end
|
|
229
235
|
|
|
230
236
|
def create_index
|
|
@@ -256,7 +262,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
256
262
|
|
|
257
263
|
FileUtils.mkdir_p("#{path}/build/tests/protocols")
|
|
258
264
|
|
|
259
|
-
@protocols.each do |doc|
|
|
265
|
+
@project_data.protocols.each do |doc|
|
|
260
266
|
img_src_dir = "#{path}/tests/protocols/#{doc.id}/img"
|
|
261
267
|
img_dst_dir = "#{path}/build/tests/protocols/#{doc.id}/img"
|
|
262
268
|
|
|
@@ -269,6 +275,17 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
269
275
|
end
|
|
270
276
|
end
|
|
271
277
|
|
|
278
|
+
def render_all_source_files
|
|
279
|
+
path = @configuration.project_root_directory
|
|
280
|
+
FileUtils.mkdir_p("#{path}/build/source_files")
|
|
281
|
+
|
|
282
|
+
@project_data.source_files.each do |doc|
|
|
283
|
+
doc.to_console
|
|
284
|
+
|
|
285
|
+
doc.to_html("#{path}/build/source_files/")
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
272
289
|
def render_index
|
|
273
290
|
path = @configuration.project_root_directory
|
|
274
291
|
|
|
@@ -279,7 +296,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
279
296
|
end
|
|
280
297
|
|
|
281
298
|
def create_search_data
|
|
282
|
-
db = SpecificationsDb.new @specifications
|
|
299
|
+
db = SpecificationsDb.new @project_data.specifications
|
|
283
300
|
data_path = "#{@configuration.project_root_directory}/build/data"
|
|
284
301
|
FileUtils.mkdir_p(data_path)
|
|
285
302
|
db.save(data_path)
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
2
|
|
|
3
3
|
class ProjectConfiguration
|
|
4
|
-
|
|
5
|
-
attr_accessor :project_root_directory
|
|
6
|
-
attr_accessor :parameters
|
|
4
|
+
attr_accessor :project_root_directory, :parameters
|
|
7
5
|
|
|
8
6
|
def initialize(path)
|
|
9
7
|
@project_root_directory = path
|
|
10
8
|
@parameters = {}
|
|
11
|
-
load_project_file
|
|
9
|
+
load_project_file
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
def load_project_file
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
rescue Psych::SyntaxError => e
|
|
13
|
+
@parameters = YAML.load_file(@project_root_directory + '/project.yml')
|
|
14
|
+
rescue Psych::SyntaxError => e
|
|
18
15
|
puts "YAML syntax error: #{e.message}"
|
|
19
|
-
|
|
20
|
-
puts
|
|
21
|
-
end
|
|
16
|
+
rescue Errno::ENOENT
|
|
17
|
+
puts 'Project file not found: project.yml'
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def get_design_inputs
|
|
25
21
|
if (@parameters.key? 'specifications') and (@parameters['specifications'].key? 'input')
|
|
26
22
|
return @parameters['specifications']['input']
|
|
27
23
|
end
|
|
28
|
-
|
|
24
|
+
|
|
25
|
+
[]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_repositories
|
|
29
|
+
return @parameters['repositories'] if @parameters.key? 'repositories'
|
|
30
|
+
|
|
31
|
+
[]
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def is_spec_db_shall_be_created
|
|
32
|
-
if
|
|
35
|
+
if @parameters.key? 'output'
|
|
33
36
|
@parameters['output'].each do |p|
|
|
34
|
-
if p == 'specifications_db'
|
|
35
|
-
return true
|
|
36
|
-
end
|
|
37
|
+
return true if p == 'specifications_db'
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
|
-
|
|
40
|
+
false
|
|
40
41
|
end
|
|
41
|
-
|
|
42
|
-
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require_relative 'doc_items/doc_item'
|
|
2
|
+
require_relative 'doc_items/heading'
|
|
3
|
+
require_relative 'doc_items/source_code_paragraph'
|
|
4
|
+
|
|
5
|
+
class SourceFileParser # rubocop:disable Style/Documentation
|
|
6
|
+
def self.parse(doc, file_lines)
|
|
7
|
+
# restart section numbering for each new document
|
|
8
|
+
Heading.reset_global_section_number
|
|
9
|
+
|
|
10
|
+
# There is no document without heading
|
|
11
|
+
title = "[#{doc.repository}] : #{doc.id}"
|
|
12
|
+
item = Heading.new(doc, title, 0)
|
|
13
|
+
doc.items.append(item)
|
|
14
|
+
doc.headings.append(item)
|
|
15
|
+
doc.title = title
|
|
16
|
+
|
|
17
|
+
item = Heading.new(doc, 'References', 2)
|
|
18
|
+
doc.items.append(item)
|
|
19
|
+
doc.headings.append(item)
|
|
20
|
+
|
|
21
|
+
# main loop
|
|
22
|
+
file_lines.each do |s| # rubocop:disable Metrics/BlockLength
|
|
23
|
+
res = %r{<REQ>(.*)</REQ>}.match(s) # Document Referece Item
|
|
24
|
+
next unless res
|
|
25
|
+
|
|
26
|
+
# extract text between <REQ> and </REQ>
|
|
27
|
+
# id will be generated automatically
|
|
28
|
+
text = res[1].strip
|
|
29
|
+
up_links = nil
|
|
30
|
+
|
|
31
|
+
# check if it contains the uplink (one or many)
|
|
32
|
+
# TODO: check this regular expression
|
|
33
|
+
first_pos = text.length # for trailing commas
|
|
34
|
+
tmp = text.scan(/(>\[(?>[^\[\]]|\g<0>)*\])/) # >[SRS-001], >[SYS-002]
|
|
35
|
+
if tmp.length.positive?
|
|
36
|
+
up_links = []
|
|
37
|
+
tmp.each do |ul|
|
|
38
|
+
lnk = ul[0]
|
|
39
|
+
#
|
|
40
|
+
doc_id = /([a-zA-Z]+)-\d+/.match(lnk) # SRS
|
|
41
|
+
up_links << lnk.upcase if doc_id
|
|
42
|
+
# try to find the real end of text
|
|
43
|
+
pos = text.index(lnk)
|
|
44
|
+
first_pos = pos if pos < first_pos
|
|
45
|
+
# remove uplink from text
|
|
46
|
+
text = text.split(lnk, 1).join('')
|
|
47
|
+
end
|
|
48
|
+
# remove trailing commas and spaces
|
|
49
|
+
if text.length > first_pos
|
|
50
|
+
first_pos -= 1
|
|
51
|
+
text = text[0..first_pos].strip
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# since we already know id and text
|
|
56
|
+
item = SourceCodeParagraph.new(doc, text)
|
|
57
|
+
|
|
58
|
+
if up_links
|
|
59
|
+
up_links.uniq! # remove duplicates
|
|
60
|
+
doc.items_with_uplinks_number += 1 # for statistics
|
|
61
|
+
up_links.each do |ul|
|
|
62
|
+
next unless tmp = />\[(\S*)\]$/.match(ul) # >[(SRS-001)]
|
|
63
|
+
|
|
64
|
+
up_link_id = tmp[1]
|
|
65
|
+
|
|
66
|
+
item.up_link_ids = [] unless item.up_link_ids
|
|
67
|
+
|
|
68
|
+
item.up_link_ids.append(up_link_id)
|
|
69
|
+
|
|
70
|
+
if tmp = /^([a-zA-Z]+)-\d+/.match(up_link_id) # SRS
|
|
71
|
+
doc.up_link_docs[tmp[1].downcase.to_s] = tmp[1].downcase # multiple documents could be up-linked
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
doc.items.append(item)
|
|
77
|
+
# for statistics
|
|
78
|
+
if doc.dictionary.has_key?(item.id.to_s)
|
|
79
|
+
doc.duplicated_ids_number += 1
|
|
80
|
+
doc.duplicates_list.append(item)
|
|
81
|
+
else
|
|
82
|
+
doc.dictionary[item.id.to_s] = item # for fast search
|
|
83
|
+
end
|
|
84
|
+
doc.controlled_items.append(item) # for fast search
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
item = Heading.new(doc, 'Source Code', 2)
|
|
88
|
+
doc.items.append(item)
|
|
89
|
+
doc.headings.append(item)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="top_nav">
|
|
12
|
-
|
|
13
|
-
<a id="index_menu_item" href="javascript:void(0)" onclick="navigate_to_home()" style="display: none;"><span><i class="fa fa-info" aria-hidden="true"></i></span> Index</a>
|
|
12
|
+
{{HOME_BUTTON}}
|
|
14
13
|
<a id="document_tree_menu_item" href="javascript:void(0)" onclick="openNav()" style="display: none;" title="Navigation Pane"><span><i class="fa fa-folder-open-o" aria-hidden="true"></i></span></a>
|
|
15
14
|
<input type="text" id="searchInput" placeholder="Search.." style="display: none;">
|
|
16
15
|
<a class="split">{{DOCUMENT_TITLE}}</a>
|
|
@@ -21,14 +21,10 @@ function openNav() {
|
|
|
21
21
|
}
|
|
22
22
|
if (document.title == "Document Index"){
|
|
23
23
|
document.getElementById('document_tree_menu_item').style.display = "none";
|
|
24
|
-
document.getElementById('home_menu_item').style.display = "block";
|
|
25
|
-
document.getElementById('index_menu_item').style.display = "none";
|
|
26
24
|
if (document.URL.includes('http')){
|
|
27
25
|
document.getElementById("searchInput").style.display = "block";
|
|
28
26
|
}
|
|
29
27
|
}else{
|
|
30
|
-
document.getElementById('home_menu_item').style.display = "none";
|
|
31
|
-
document.getElementById('index_menu_item').style.display = "block";
|
|
32
28
|
if (document.title.includes("Traceability Matrix:")){
|
|
33
29
|
document.getElementById('document_tree_menu_item').style.display = "none";
|
|
34
30
|
} else {
|
|
@@ -63,19 +59,6 @@ function openNav() {
|
|
|
63
59
|
document.getElementById(required_id).style.display = 'block';
|
|
64
60
|
}
|
|
65
61
|
|
|
66
|
-
function navigate_to_home(){
|
|
67
|
-
if (document.title != "Document Index")
|
|
68
|
-
{
|
|
69
|
-
if (document.URL.includes('protocols')){
|
|
70
|
-
window.location.href = "./../../../index.html";
|
|
71
|
-
}else{
|
|
72
|
-
window.location.href = "./../../index.html";
|
|
73
|
-
}
|
|
74
|
-
}else{
|
|
75
|
-
window.location.href = "./index.html";
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
62
|
// Modal window for image zoom
|
|
80
63
|
function image_OnClick(clicked){
|
|
81
64
|
|
metadata
CHANGED
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: Almirah
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oleksandr Ivanov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rouge
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '4.6'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 4.6.1
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '4.6'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 4.6.1
|
|
13
32
|
- !ruby/object:Gem::Dependency
|
|
14
33
|
name: thor
|
|
15
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,24 +72,30 @@ files:
|
|
|
53
72
|
- lib/almirah/doc_items/markdown_list.rb
|
|
54
73
|
- lib/almirah/doc_items/markdown_table.rb
|
|
55
74
|
- lib/almirah/doc_items/paragraph.rb
|
|
75
|
+
- lib/almirah/doc_items/source_code_paragraph.rb
|
|
56
76
|
- lib/almirah/doc_items/text_line.rb
|
|
57
77
|
- lib/almirah/doc_items/todo_block.rb
|
|
58
78
|
- lib/almirah/doc_parser.rb
|
|
59
79
|
- lib/almirah/doc_types/base_document.rb
|
|
60
80
|
- lib/almirah/doc_types/coverage.rb
|
|
81
|
+
- lib/almirah/doc_types/implementation.rb
|
|
61
82
|
- lib/almirah/doc_types/index.rb
|
|
62
83
|
- lib/almirah/doc_types/persistent_document.rb
|
|
63
84
|
- lib/almirah/doc_types/protocol.rb
|
|
85
|
+
- lib/almirah/doc_types/source_file.rb
|
|
64
86
|
- lib/almirah/doc_types/specification.rb
|
|
65
87
|
- lib/almirah/doc_types/traceability.rb
|
|
66
88
|
- lib/almirah/dom/doc_section.rb
|
|
67
89
|
- lib/almirah/dom/document.rb
|
|
68
90
|
- lib/almirah/navigation_pane.rb
|
|
69
91
|
- lib/almirah/project.rb
|
|
92
|
+
- lib/almirah/project/doc_linker.rb
|
|
93
|
+
- lib/almirah/project/project_data.rb
|
|
70
94
|
- lib/almirah/project_configuration.rb
|
|
71
95
|
- lib/almirah/project_template.rb
|
|
72
96
|
- lib/almirah/project_utility.rb
|
|
73
97
|
- lib/almirah/search/specifications_db.rb
|
|
98
|
+
- lib/almirah/source_file_parser.rb
|
|
74
99
|
- lib/almirah/templates/css/main.css
|
|
75
100
|
- lib/almirah/templates/css/search.css
|
|
76
101
|
- lib/almirah/templates/page.html
|
|
@@ -80,7 +105,6 @@ homepage: http://almirah.site
|
|
|
80
105
|
licenses:
|
|
81
106
|
- MIT
|
|
82
107
|
metadata: {}
|
|
83
|
-
post_install_message:
|
|
84
108
|
rdoc_options: []
|
|
85
109
|
require_paths:
|
|
86
110
|
- lib
|
|
@@ -95,8 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
95
119
|
- !ruby/object:Gem::Version
|
|
96
120
|
version: '0'
|
|
97
121
|
requirements: []
|
|
98
|
-
rubygems_version:
|
|
99
|
-
signing_key:
|
|
122
|
+
rubygems_version: 4.0.1
|
|
100
123
|
specification_version: 4
|
|
101
124
|
summary: Almirah
|
|
102
125
|
test_files: []
|