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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04afed7a81d07d1789a115c7c31e4d09df1c638eaf2eacf390f7ec7611e522ef
4
- data.tar.gz: 403f2c7cb9381b4ee12859b27f9fc584ce73274ced9254a7b08b5cda4b316849
3
+ metadata.gz: 7a37e09f530d619a6d25515c239174c04cf8a10d1c8293744f68353501bd72b0
4
+ data.tar.gz: afafdfbfb47738901370354449fcc798e4cc8061a12a3b055da4ce49a4b2e66d
5
5
  SHA512:
6
- metadata.gz: 3db8f92b780657bc4109b96dc64f19c28249c509ba9a7d8c33b71c37fa6fb821a7a140d32bd955e20a7f49e5401b55d64d8732cccd2c7a5800de494dc05660db
7
- data.tar.gz: eeab5aa32a82b0efbe0cc3c3f1e576cd99b9ec1eadd9e5b1e46b7e5616d8c715d8b52640a83e9e14ce47ec443239a477f3cc8488441b356dceda6a88a22e306e
6
+ metadata.gz: 0efb3ebcb05f9c74845371c7b1dc51891854c080b227f669413fd5759d076432bff58610aa1a7658cd003432c8d894c4f2eaf48869436852002fecafd2a6fa1c
7
+ data.tar.gz: de0c2bb8290e904df8f23d0c83dabd0d93e2ab9c05feda2827e29be8220b93960211bc291b187f50f7e841db26619e074c8ac09df4ffa638296ebfc4f5965103
@@ -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.parse_document(doc)
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=\"./../#{up_link_doc_name}/#{up_link_doc_name}.html##{@up_link_ids[0]}\" \
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=\"./../#{up_link_doc_name}/#{up_link_doc_name}.html##{lnk}\" \
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>'
@@ -1,5 +1,6 @@
1
1
  require_relative 'doc_item'
2
2
 
3
+ # <REQ> Implementa a non-controlled paragraph as a subclass of the DocItem >[SRS-004] </REQ>
3
4
  class Paragraph < DocItem
4
5
  attr_accessor :text
5
6
 
@@ -0,0 +1,7 @@
1
+ class SourceCodeParagraph < ControlledParagraph
2
+ @@source_code_links_counter = 'AAAA'
3
+ def initialize(doc, text)
4
+ super(doc, text, "SC-#{@@source_code_links_counter}")
5
+ @@source_code_links_counter.next!
6
+ end
7
+ end
@@ -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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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
@@ -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 :specifications, :protocols, :traceability_matrices, :coverage_matrices, :specifications_dictionary,
12
- :index, :project, :configuration
14
+ attr_accessor :index, :project, :configuration, :project_data
13
15
 
14
16
  def initialize(configuration)
15
17
  @configuration = configuration
16
- @specifications = []
17
- @protocols = []
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
- begin
16
- @parameters = YAML.load_file(@project_root_directory + '/project.yml')
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
- rescue Errno::ENOENT
20
- puts "Project file not found: project.yml"
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
- return []
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 (@parameters.key? 'output')
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
- return false
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
- <a id="home_menu_item" href="javascript:void(0)" onclick="navigate_to_home()"><span><i class="fa fa-home" aria-hidden="true"></i></span>&nbsp;Home</a>
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>&nbsp;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.2.8
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: 2024-10-28 00:00:00.000000000 Z
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: 3.5.17
99
- signing_key:
122
+ rubygems_version: 4.0.1
100
123
  specification_version: 4
101
124
  summary: Almirah
102
125
  test_files: []