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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04afed7a81d07d1789a115c7c31e4d09df1c638eaf2eacf390f7ec7611e522ef
4
- data.tar.gz: 403f2c7cb9381b4ee12859b27f9fc584ce73274ced9254a7b08b5cda4b316849
3
+ metadata.gz: be9e792ca43617f09611a801b8e8c8efde741ec531a97295de8d65378232c0d2
4
+ data.tar.gz: 492000cb9aadaf0731e077b46ec3a32b573da9c8cd2acfaff6f282dec75c394d
5
5
  SHA512:
6
- metadata.gz: 3db8f92b780657bc4109b96dc64f19c28249c509ba9a7d8c33b71c37fa6fb821a7a140d32bd955e20a7f49e5401b55d64d8732cccd2c7a5800de494dc05660db
7
- data.tar.gz: eeab5aa32a82b0efbe0cc3c3f1e576cd99b9ec1eadd9e5b1e46b7e5616d8c715d8b52640a83e9e14ce47ec443239a477f3cc8488441b356dceda6a88a22e306e
6
+ metadata.gz: 51e65415ee9d0a8c0f77d19d21ab525a01cbc4c025bc5572f92f2df13bc56ceed8b04be5e18d3c0308c468c43986dd267046d9b1c125e281ad01476b08a93569
7
+ data.tar.gz: b64d9b5a0da5d254ecbc6dd861db05017b8cd81e13c0f58071413816d9b20d879187cbda5b2a703d5eae9f92ec044000010fc6b1b103fa30a9de04b265562cde
data/bin/almirah CHANGED
File without changes
@@ -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
@@ -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
- 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.1
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: []