Almirah 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- 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 +74 -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: 7a37e09f530d619a6d25515c239174c04cf8a10d1c8293744f68353501bd72b0
|
|
4
|
+
data.tar.gz: afafdfbfb47738901370354449fcc798e4cc8061a12a3b055da4ce49a4b2e66d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0efb3ebcb05f9c74845371c7b1dc51891854c080b227f669413fd5759d076432bff58610aa1a7658cd003432c8d894c4f2eaf48869436852002fecafd2a6fa1c
|
|
7
|
+
data.tar.gz: de0c2bb8290e904df8f23d0c83dabd0d93e2ab9c05feda2827e29be8220b93960211bc291b187f50f7e841db26619e074c8ac09df4ffa638296ebfc4f5965103
|
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
|
|
@@ -61,9 +63,9 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
61
63
|
link_all_protocols
|
|
62
64
|
check_wrong_specification_referenced
|
|
63
65
|
create_index
|
|
64
|
-
render_all_specifications(@specifications)
|
|
65
|
-
render_all_specifications(@traceability_matrices)
|
|
66
|
-
render_all_specifications(@coverage_matrices)
|
|
66
|
+
render_all_specifications(@project_data.specifications)
|
|
67
|
+
render_all_specifications(@project_data.traceability_matrices)
|
|
68
|
+
render_all_specifications(@project_data.coverage_matrices)
|
|
67
69
|
render_all_protocols
|
|
68
70
|
render_index
|
|
69
71
|
create_search_data
|
|
@@ -78,8 +80,8 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
78
80
|
# parse documents in the second pass
|
|
79
81
|
Dir.glob("#{path}/specifications/**/*.md").each do |f| # rubocop:disable Style/CombinableLoops
|
|
80
82
|
doc = DocFabric.create_specification(f)
|
|
81
|
-
@specifications.append(doc)
|
|
82
|
-
@specifications_dictionary[doc.id.to_s.downcase] = doc
|
|
83
|
+
@project_data.specifications.append(doc)
|
|
84
|
+
@project_data.specifications_dictionary[doc.id.to_s.downcase] = doc
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
|
|
@@ -87,7 +89,24 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
87
89
|
path = @configuration.project_root_directory
|
|
88
90
|
Dir.glob("#{path}/tests/protocols/**/*.md").each do |f|
|
|
89
91
|
doc = DocFabric.create_protocol(f)
|
|
90
|
-
@protocols.append(doc)
|
|
92
|
+
@project_data.protocols.append(doc)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def parse_all_source_files
|
|
97
|
+
@configuration.get_repositories.each do |repos|
|
|
98
|
+
# puts "Processing repository: #{repos['name']}, #{repos['path']}"
|
|
99
|
+
next unless repos['path'] && Dir.exist?(repos['path'])
|
|
100
|
+
|
|
101
|
+
file_path = repos['path']
|
|
102
|
+
Dir.glob("#{repos['path']}/**/*.*").each do |f|
|
|
103
|
+
next unless File.file?(f) && f.end_with?('.c', '.cpp', '.h', '.hpp', '.py', '.java', '.rb', '.js', '.ts', '.go',
|
|
104
|
+
'.rs')
|
|
105
|
+
|
|
106
|
+
doc = DocFabric.create_source_file(file_path, f, repos['name'])
|
|
107
|
+
# puts "Source file: #{doc.id}"
|
|
108
|
+
@project_data.source_files.append(doc)
|
|
109
|
+
end
|
|
91
110
|
end
|
|
92
111
|
end
|
|
93
112
|
|
|
@@ -95,52 +114,62 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
95
114
|
path = @configuration.project_root_directory
|
|
96
115
|
Dir.glob("#{path}/tests/runs/#{test_run}/**/*.md").each do |f|
|
|
97
116
|
doc = DocFabric.create_protocol(f)
|
|
98
|
-
@protocols.append(doc)
|
|
117
|
+
@project_data.protocols.append(doc)
|
|
99
118
|
end
|
|
100
119
|
end
|
|
101
120
|
|
|
102
|
-
def link_all_specifications # rubocop:disable Metrics/MethodLength
|
|
103
|
-
comb_list = @specifications.combination(2)
|
|
121
|
+
def link_all_specifications # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
122
|
+
comb_list = @project_data.specifications.combination(2)
|
|
104
123
|
comb_list.each do |c|
|
|
105
124
|
link_two_specifications(c[0], c[1])
|
|
106
125
|
# puts "Link: #{c[0].id} - #{c[1].id}"
|
|
107
126
|
end
|
|
108
127
|
# separatelly create design inputs treceability
|
|
109
128
|
@configuration.get_design_inputs.each do |i|
|
|
110
|
-
next unless @specifications_dictionary.key? i.to_s.downcase
|
|
129
|
+
next unless @project_data.specifications_dictionary.key? i.to_s.downcase
|
|
111
130
|
|
|
112
|
-
document = @specifications_dictionary[i.to_s.downcase]
|
|
131
|
+
document = @project_data.specifications_dictionary[i.to_s.downcase]
|
|
113
132
|
if document
|
|
114
133
|
doc = DocFabric.create_traceability_document(document, nil)
|
|
115
|
-
@traceability_matrices.append doc
|
|
134
|
+
@project_data.traceability_matrices.append doc
|
|
116
135
|
end
|
|
117
136
|
end
|
|
118
137
|
end
|
|
119
138
|
|
|
120
139
|
def link_all_protocols # rubocop:disable Metrics/MethodLength
|
|
121
|
-
@protocols.each do |p|
|
|
122
|
-
@specifications.each do |s|
|
|
140
|
+
@project_data.protocols.each do |p|
|
|
141
|
+
@project_data.specifications.each do |s|
|
|
123
142
|
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
|
|
143
|
+
DocLinker.link_protocol_to_spec(p, s)
|
|
144
|
+
@project_data.covered_specifications_dictionary[s.id.to_s] = s
|
|
126
145
|
end
|
|
127
146
|
end
|
|
128
147
|
end
|
|
129
148
|
# create coverage documents
|
|
130
|
-
@covered_specifications_dictionary.each do |_key, value|
|
|
149
|
+
@project_data.covered_specifications_dictionary.each do |_key, value|
|
|
131
150
|
doc = DocFabric.create_coverage_matrix(value)
|
|
132
|
-
@coverage_matrices.append doc
|
|
151
|
+
@project_data.coverage_matrices.append doc
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def link_all_source_files
|
|
156
|
+
return unless DocLinker.link_all_source_files(@project_data)
|
|
157
|
+
|
|
158
|
+
# create implementation documents
|
|
159
|
+
@project_data.implemented_specifications_dictionary.each do |_key, value|
|
|
160
|
+
doc = DocFabric.create_implementation_document(value)
|
|
161
|
+
@project_data.implementation_matrices.append doc
|
|
133
162
|
end
|
|
134
163
|
end
|
|
135
164
|
|
|
136
165
|
def check_wrong_specification_referenced # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
137
166
|
available_specification_ids = {}
|
|
138
167
|
|
|
139
|
-
@specifications.each do |s|
|
|
168
|
+
@project_data.specifications.each do |s|
|
|
140
169
|
available_specification_ids[s.id.to_s.downcase] = s
|
|
141
170
|
end
|
|
142
171
|
|
|
143
|
-
@specifications.each do |s| # rubocop:disable Style/CombinableLoops
|
|
172
|
+
@project_data.specifications.each do |s| # rubocop:disable Style/CombinableLoops
|
|
144
173
|
s.up_link_docs.each do |key, _value|
|
|
145
174
|
next if available_specification_ids.key?(key)
|
|
146
175
|
|
|
@@ -197,34 +226,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
197
226
|
end
|
|
198
227
|
# create treceability document
|
|
199
228
|
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
|
|
229
|
+
@project_data.traceability_matrices.append doc
|
|
228
230
|
end
|
|
229
231
|
|
|
230
232
|
def create_index
|
|
@@ -256,7 +258,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
256
258
|
|
|
257
259
|
FileUtils.mkdir_p("#{path}/build/tests/protocols")
|
|
258
260
|
|
|
259
|
-
@protocols.each do |doc|
|
|
261
|
+
@project_data.protocols.each do |doc|
|
|
260
262
|
img_src_dir = "#{path}/tests/protocols/#{doc.id}/img"
|
|
261
263
|
img_dst_dir = "#{path}/build/tests/protocols/#{doc.id}/img"
|
|
262
264
|
|
|
@@ -269,6 +271,17 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
269
271
|
end
|
|
270
272
|
end
|
|
271
273
|
|
|
274
|
+
def render_all_source_files
|
|
275
|
+
path = @configuration.project_root_directory
|
|
276
|
+
FileUtils.mkdir_p("#{path}/build/source_files")
|
|
277
|
+
|
|
278
|
+
@project_data.source_files.each do |doc|
|
|
279
|
+
doc.to_console
|
|
280
|
+
|
|
281
|
+
doc.to_html("#{path}/build/source_files/")
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
272
285
|
def render_index
|
|
273
286
|
path = @configuration.project_root_directory
|
|
274
287
|
|
|
@@ -279,7 +292,7 @@ class Project # rubocop:disable Metrics/ClassLength,Style/Documentation
|
|
|
279
292
|
end
|
|
280
293
|
|
|
281
294
|
def create_search_data
|
|
282
|
-
db = SpecificationsDb.new @specifications
|
|
295
|
+
db = SpecificationsDb.new @project_data.specifications
|
|
283
296
|
data_path = "#{@configuration.project_root_directory}/build/data"
|
|
284
297
|
FileUtils.mkdir_p(data_path)
|
|
285
298
|
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.0
|
|
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: []
|