expressir 2.1.21 → 2.1.23
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/.rubocop.yml +9 -9
- data/.rubocop_todo.yml +62 -50
- data/Gemfile +2 -2
- data/README.adoc +364 -7
- data/bin/rspec +2 -1
- data/docs/liquid_drops.adoc +1 -1
- data/expressir.gemspec +0 -2
- data/lib/expressir/benchmark.rb +6 -3
- data/lib/expressir/cli.rb +24 -11
- data/lib/expressir/commands/benchmark.rb +7 -16
- data/lib/expressir/commands/benchmark_cache.rb +9 -17
- data/lib/expressir/commands/coverage.rb +125 -55
- data/lib/expressir/coverage.rb +100 -26
- data/lib/expressir/express/cache.rb +2 -1
- data/lib/expressir/express/formatter.rb +54 -12
- data/lib/expressir/express/hyperlink_formatter.rb +2 -1
- data/lib/expressir/express/parser.rb +329 -112
- data/lib/expressir/express/schema_head_formatter.rb +2 -1
- data/lib/expressir/express/visitor.rb +114 -51
- data/lib/expressir/model/declarations/entity.rb +2 -1
- data/lib/expressir/model/declarations/informal_proposition_rule.rb +24 -0
- data/lib/expressir/model/declarations/rule.rb +2 -1
- data/lib/expressir/model/declarations/schema.rb +4 -1
- data/lib/expressir/model/declarations/type.rb +2 -1
- data/lib/expressir/model/identifier.rb +2 -1
- data/lib/expressir/model/literals/string.rb +2 -1
- data/lib/expressir/model/model_element.rb +84 -145
- data/lib/expressir/model/repository.rb +2 -1
- data/lib/expressir/schema_manifest.rb +150 -0
- data/lib/expressir/schema_manifest_entry.rb +17 -0
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +29 -12
- metadata +5 -2
data/lib/expressir/cli.rb
CHANGED
@@ -24,26 +24,33 @@ module Expressir
|
|
24
24
|
end
|
25
25
|
|
26
26
|
desc "clean PATH", "Strip remarks and prettify EXPRESS schema at PATH"
|
27
|
-
method_option :output, type: :string,
|
27
|
+
method_option :output, type: :string,
|
28
|
+
desc: "Output file path (defaults to stdout)"
|
28
29
|
def clean(path)
|
29
30
|
Commands::Clean.new(options).run(path)
|
30
31
|
end
|
31
32
|
|
32
|
-
desc "benchmark FILE_OR_YAML",
|
33
|
-
|
33
|
+
desc "benchmark FILE_OR_YAML",
|
34
|
+
"Benchmark schema loading performance for a file or list of files from YAML"
|
35
|
+
method_option :ips, type: :boolean,
|
36
|
+
desc: "Use benchmark-ips for detailed statistics"
|
34
37
|
method_option :verbose, type: :boolean, desc: "Show verbose output"
|
35
38
|
method_option :save, type: :boolean, desc: "Save benchmark results to file"
|
36
|
-
method_option :format, type: :string,
|
39
|
+
method_option :format, type: :string,
|
40
|
+
desc: "Output format (json, csv, default)"
|
37
41
|
def benchmark(path)
|
38
42
|
Commands::Benchmark.new(options).run(path)
|
39
43
|
end
|
40
44
|
|
41
45
|
desc "benchmark-cache FILE_OR_YAML", "Benchmark schema loading with caching"
|
42
|
-
method_option :ips, type: :boolean,
|
46
|
+
method_option :ips, type: :boolean,
|
47
|
+
desc: "Use benchmark-ips for detailed statistics"
|
43
48
|
method_option :verbose, type: :boolean, desc: "Show verbose output"
|
44
49
|
method_option :save, type: :boolean, desc: "Save benchmark results to file"
|
45
|
-
method_option :format, type: :string,
|
46
|
-
|
50
|
+
method_option :format, type: :string,
|
51
|
+
desc: "Output format (json, csv, default)"
|
52
|
+
method_option :cache_path, type: :string,
|
53
|
+
desc: "Path to store the cache file"
|
47
54
|
def benchmark_cache(path)
|
48
55
|
Commands::BenchmarkCache.new(options).run(path)
|
49
56
|
end
|
@@ -53,10 +60,16 @@ module Expressir
|
|
53
60
|
Commands::Validate.new(options).run(paths)
|
54
61
|
end
|
55
62
|
|
56
|
-
desc "coverage *PATH",
|
57
|
-
|
58
|
-
method_option :
|
59
|
-
|
63
|
+
desc "coverage *PATH",
|
64
|
+
"List EXPRESS entities and check documentation coverage"
|
65
|
+
method_option :format, type: :string,
|
66
|
+
desc: "Output format (text, json, yaml)", default: "text"
|
67
|
+
method_option :exclude, type: :string,
|
68
|
+
desc: "Comma-separated list of EXPRESS entity types to skip from coverage (e.g., TYPE,CONSTANT,TYPE:SELECT)"
|
69
|
+
method_option :output, type: :string,
|
70
|
+
desc: "Output file path for JSON/YAML formats (defaults to coverage_report.json/yaml)"
|
71
|
+
method_option :ignore_files, type: :string,
|
72
|
+
desc: "Path to YAML file containing array of files to ignore from overall coverage calculation"
|
60
73
|
def coverage(*paths)
|
61
74
|
Commands::Coverage.new(options).run(paths)
|
62
75
|
end
|
@@ -5,7 +5,7 @@ module Expressir
|
|
5
5
|
configure_benchmarking
|
6
6
|
|
7
7
|
if [".yml", ".yaml"].include?(File.extname(path).downcase)
|
8
|
-
|
8
|
+
benchmark_from_manifest(path)
|
9
9
|
else
|
10
10
|
benchmark_file(path)
|
11
11
|
end
|
@@ -36,24 +36,15 @@ module Expressir
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
say "Express Schema Loading Benchmark from
|
39
|
+
def benchmark_from_manifest(manifest_path)
|
40
|
+
say "Express Schema Loading Benchmark from Manifest"
|
41
41
|
say "--------------------------------"
|
42
42
|
|
43
|
-
# Load schema
|
44
|
-
|
45
|
-
|
46
|
-
# Handle format: { "schemas": ["path1", "path2", ...] }
|
47
|
-
schema_files = schema_list["schemas"]
|
48
|
-
elsif schema_list.is_a?(Array)
|
49
|
-
# Handle format: ["path1", "path2", ...]
|
50
|
-
schema_files = schema_list
|
51
|
-
else
|
52
|
-
say "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
|
53
|
-
return
|
54
|
-
end
|
43
|
+
# Load schema manifest
|
44
|
+
manifest = Expressir::SchemaManifest.from_file(manifest_path)
|
45
|
+
schema_files = manifest.schemas.map(&:path)
|
55
46
|
|
56
|
-
say "
|
47
|
+
say "Manifest File: #{manifest_path}"
|
57
48
|
say "Number of schemas in list: #{schema_files.size}"
|
58
49
|
say "--------------------------------"
|
59
50
|
|
@@ -8,7 +8,7 @@ module Expressir
|
|
8
8
|
cache_path = options[:cache_path] || generate_temp_cache_path
|
9
9
|
|
10
10
|
if [".yml", ".yaml"].include?(File.extname(path).downcase)
|
11
|
-
|
11
|
+
benchmark_cache_from_manifest(path, cache_path)
|
12
12
|
else
|
13
13
|
benchmark_cache_file(path, cache_path)
|
14
14
|
end
|
@@ -32,7 +32,8 @@ module Expressir
|
|
32
32
|
say "--------------------------------"
|
33
33
|
|
34
34
|
# Benchmark with caching
|
35
|
-
results = Expressir::Benchmark.measure_with_cache(path,
|
35
|
+
results = Expressir::Benchmark.measure_with_cache(path,
|
36
|
+
cache_path) do |file|
|
36
37
|
Expressir::Express::Parser.from_file(file)
|
37
38
|
end
|
38
39
|
|
@@ -44,24 +45,15 @@ module Expressir
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
def
|
48
|
-
say "Express Schema Loading Benchmark with Caching from
|
48
|
+
def benchmark_cache_from_manifest(manifest_path, cache_path)
|
49
|
+
say "Express Schema Loading Benchmark with Caching from Manifest"
|
49
50
|
say "--------------------------------"
|
50
51
|
|
51
|
-
# Load schema
|
52
|
-
|
53
|
-
|
54
|
-
# Handle format: { "schemas": ["path1", "path2", ...] }
|
55
|
-
schema_files = schema_list["schemas"]
|
56
|
-
elsif schema_list.is_a?(Array)
|
57
|
-
# Handle format: ["path1", "path2", ...]
|
58
|
-
schema_files = schema_list
|
59
|
-
else
|
60
|
-
say "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
|
61
|
-
return
|
62
|
-
end
|
52
|
+
# Load schema manifest
|
53
|
+
manifest = Expressir::SchemaManifest.from_file(manifest_path)
|
54
|
+
schema_files = manifest.schemas.map(&:path)
|
63
55
|
|
64
|
-
say "
|
56
|
+
say "Manifest File: #{manifest_path}"
|
65
57
|
say "Number of schemas in list: #{schema_files.size}"
|
66
58
|
say "Cache: #{cache_path}"
|
67
59
|
say "--------------------------------"
|
@@ -32,27 +32,28 @@ module Expressir
|
|
32
32
|
|
33
33
|
def collect_reports(paths)
|
34
34
|
reports = []
|
35
|
+
ignored_files = parse_ignore_files
|
35
36
|
|
36
37
|
paths.each do |path|
|
37
|
-
handle_path(path, reports)
|
38
|
+
handle_path(path, reports, ignored_files)
|
38
39
|
end
|
39
40
|
|
40
41
|
reports
|
41
42
|
end
|
42
43
|
|
43
|
-
def handle_path(path, reports)
|
44
|
+
def handle_path(path, reports, ignored_files)
|
44
45
|
if File.directory?(path)
|
45
|
-
handle_directory(path, reports)
|
46
|
+
handle_directory(path, reports, ignored_files)
|
46
47
|
elsif File.extname(path).downcase == ".exp"
|
47
|
-
handle_express_file(path, reports)
|
48
|
+
handle_express_file(path, reports, ignored_files)
|
48
49
|
elsif [".yml", ".yaml"].include?(File.extname(path).downcase)
|
49
|
-
|
50
|
+
handle_schema_manifest(path, reports, ignored_files)
|
50
51
|
else
|
51
52
|
say "Unsupported file type: #{path}"
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
|
-
def handle_directory(path, reports)
|
56
|
+
def handle_directory(path, reports, ignored_files)
|
56
57
|
say "Processing directory: #{path}"
|
57
58
|
exp_files = Dir.glob(File.join(path, "**", "*.exp"))
|
58
59
|
if exp_files.empty?
|
@@ -79,54 +80,38 @@ module Expressir
|
|
79
80
|
progress.increment
|
80
81
|
end
|
81
82
|
skip_types = parse_skip_types
|
82
|
-
report = Expressir::Coverage::Report.from_repository(
|
83
|
+
report = Expressir::Coverage::Report.from_repository(
|
84
|
+
repository,
|
85
|
+
skip_types,
|
86
|
+
ignored_files,
|
87
|
+
)
|
83
88
|
reports << report
|
84
89
|
rescue StandardError => e
|
85
90
|
say "Error processing directory #{path}: #{e.message}"
|
86
91
|
end
|
87
92
|
end
|
88
93
|
|
89
|
-
def handle_express_file(path, reports)
|
94
|
+
def handle_express_file(path, reports, ignored_files)
|
90
95
|
say "Processing file: #{path}"
|
91
96
|
begin
|
92
97
|
# For a single file, we don't need a progress bar
|
93
98
|
skip_types = parse_skip_types
|
94
|
-
report = Expressir::Coverage::Report.from_file(path, skip_types
|
99
|
+
report = Expressir::Coverage::Report.from_file(path, skip_types,
|
100
|
+
ignored_files)
|
95
101
|
reports << report
|
96
102
|
rescue StandardError => e
|
97
103
|
say "Error processing file #{path}: #{e.message}"
|
98
104
|
end
|
99
105
|
end
|
100
106
|
|
101
|
-
def
|
102
|
-
say "Processing
|
107
|
+
def handle_schema_manifest(path, reports, ignored_files)
|
108
|
+
say "Processing schema manifest: #{path}"
|
103
109
|
begin
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
# Handle the nested structure with schema name keys and path values
|
111
|
-
if schemas_data.is_a?(Hash)
|
112
|
-
schema_files = schemas_data.values.map do |schema_data|
|
113
|
-
if schema_data.is_a?(Hash) && schema_data["path"]
|
114
|
-
# Make path relative to the manifest location
|
115
|
-
File.expand_path(schema_data["path"], manifest_dir)
|
116
|
-
end
|
117
|
-
end.compact
|
118
|
-
|
119
|
-
say "Found #{schema_files.size} schema files to process"
|
120
|
-
else
|
121
|
-
# If it's a direct array of paths (old format)
|
122
|
-
schema_files = schemas_data
|
123
|
-
end
|
124
|
-
elsif schema_list.is_a?(Array)
|
125
|
-
schema_files = schema_list
|
126
|
-
else
|
127
|
-
say "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
|
128
|
-
return
|
129
|
-
end
|
110
|
+
# Load schema manifest
|
111
|
+
manifest = Expressir::SchemaManifest.from_file(path)
|
112
|
+
schema_files = manifest.schemas.map(&:path)
|
113
|
+
|
114
|
+
say "Found #{schema_files.size} schema files to process"
|
130
115
|
|
131
116
|
# Initialize progress bar
|
132
117
|
if schema_files && !schema_files.empty?
|
@@ -149,12 +134,15 @@ module Expressir
|
|
149
134
|
|
150
135
|
# Create and add the report
|
151
136
|
skip_types = parse_skip_types
|
152
|
-
report = Expressir::Coverage::Report.from_repository(
|
137
|
+
report = Expressir::Coverage::Report.from_repository(
|
138
|
+
repository,
|
139
|
+
skip_types,
|
140
|
+
ignored_files,
|
141
|
+
)
|
153
142
|
reports << report
|
154
143
|
end
|
155
144
|
rescue StandardError => e
|
156
|
-
say "Error processing
|
157
|
-
say "Debug: schema_list structure: #{schema_list.class}" if schema_list
|
145
|
+
say "Error processing schema manifest #{path}: #{e.message}"
|
158
146
|
end
|
159
147
|
end
|
160
148
|
|
@@ -199,7 +187,8 @@ module Expressir
|
|
199
187
|
# Add rows
|
200
188
|
dirs.each do |dir, stats|
|
201
189
|
coverage = stats["total"].positive? ? (stats["documented"].to_f / stats["total"] * 100).round(2) : 100.0
|
202
|
-
table.add_row [dir, stats["total"], stats["documented"],
|
190
|
+
table.add_row [dir, stats["total"], stats["documented"],
|
191
|
+
"#{coverage}%"]
|
203
192
|
end
|
204
193
|
|
205
194
|
say table
|
@@ -249,34 +238,66 @@ module Expressir
|
|
249
238
|
},
|
250
239
|
)
|
251
240
|
|
252
|
-
table.add_row ["Coverage Percentage",
|
241
|
+
table.add_row ["Coverage Percentage",
|
242
|
+
"#{overall['coverage_percentage']}%"]
|
253
243
|
table.add_row ["Total Entities", overall["total_entities"]]
|
254
244
|
table.add_row ["Documented Entities", overall["documented_entities"]]
|
255
|
-
table.add_row ["Undocumented Entities",
|
245
|
+
table.add_row ["Undocumented Entities",
|
246
|
+
overall["undocumented_entities"]]
|
256
247
|
|
257
248
|
say table
|
258
249
|
end
|
259
250
|
|
260
251
|
def build_structured_report(reports)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
252
|
+
# Calculate ignored file statistics
|
253
|
+
ignored_files = reports.flat_map(&:ignored_file_reports)
|
254
|
+
ignored_entities_count = ignored_files.sum { |f| f["total"] }
|
255
|
+
|
256
|
+
overall_stats = {
|
257
|
+
"total_entities" => reports.sum { |r| r.total_entities.size },
|
258
|
+
"documented_entities" => reports.sum do |r|
|
259
|
+
r.documented_entities.size
|
260
|
+
end,
|
261
|
+
"undocumented_entities" => reports.sum do |r|
|
262
|
+
r.undocumented_entities.size
|
263
|
+
end,
|
264
|
+
"coverage_percentage" => if reports.sum do |r|
|
265
|
+
r.total_entities.size
|
266
|
+
end.positive?
|
267
|
+
(reports.sum do |r|
|
268
|
+
r.documented_entities.size
|
269
|
+
end.to_f / reports.sum do |r|
|
270
|
+
r.total_entities.size
|
271
|
+
end * 100).round(2)
|
272
|
+
else
|
273
|
+
100.0
|
274
|
+
end,
|
275
|
+
}
|
276
|
+
|
277
|
+
# Add ignored file information if there are any
|
278
|
+
if ignored_files.any?
|
279
|
+
overall_stats["ignored_files_count"] = ignored_files.size
|
280
|
+
overall_stats["ignored_entities_count"] = ignored_entities_count
|
281
|
+
end
|
282
|
+
|
283
|
+
structured_report = {
|
284
|
+
"overall" => overall_stats,
|
272
285
|
"files" => reports.flat_map(&:file_reports),
|
273
286
|
"directories" => reports.flat_map(&:directory_reports),
|
274
287
|
}
|
288
|
+
|
289
|
+
# Add ignored files section if there are any
|
290
|
+
if ignored_files.any?
|
291
|
+
structured_report["ignored_files"] = ignored_files
|
292
|
+
end
|
293
|
+
|
294
|
+
structured_report
|
275
295
|
end
|
276
296
|
|
277
297
|
def display_json_output(reports)
|
278
298
|
output_file = options[:output] || "coverage_report.json"
|
279
|
-
File.write(output_file,
|
299
|
+
File.write(output_file,
|
300
|
+
JSON.pretty_generate(build_structured_report(reports)))
|
280
301
|
say "JSON coverage report written to: #{output_file}"
|
281
302
|
end
|
282
303
|
|
@@ -343,6 +364,55 @@ module Expressir
|
|
343
364
|
end
|
344
365
|
end
|
345
366
|
end
|
367
|
+
|
368
|
+
# Parse and expand ignore files from YAML
|
369
|
+
# @return [Hash] Hash mapping absolute file paths to their matched patterns
|
370
|
+
def parse_ignore_files
|
371
|
+
ignore_files_option = options["ignore_files"] || options[:ignore_files]
|
372
|
+
return {} unless ignore_files_option
|
373
|
+
|
374
|
+
unless File.exist?(ignore_files_option)
|
375
|
+
say "Warning: Ignore files YAML not found: #{ignore_files_option}"
|
376
|
+
return {}
|
377
|
+
end
|
378
|
+
|
379
|
+
begin
|
380
|
+
patterns = YAML.load_file(ignore_files_option)
|
381
|
+
unless patterns.is_a?(Array)
|
382
|
+
say "Warning: Invalid ignore files YAML format. Expected an array of file patterns."
|
383
|
+
return {}
|
384
|
+
end
|
385
|
+
|
386
|
+
ignore_files_dir = File.dirname(File.expand_path(ignore_files_option))
|
387
|
+
expanded_files = {}
|
388
|
+
|
389
|
+
patterns.each do |pattern|
|
390
|
+
# Resolve pattern relative to the YAML file's directory
|
391
|
+
full_pattern = File.expand_path(pattern, ignore_files_dir)
|
392
|
+
|
393
|
+
# Expand glob pattern
|
394
|
+
matched_files = Dir.glob(full_pattern)
|
395
|
+
|
396
|
+
if matched_files.empty?
|
397
|
+
say "Warning: No files matched pattern: #{pattern}"
|
398
|
+
else
|
399
|
+
matched_files.each do |file_path|
|
400
|
+
# Store absolute path and the original pattern that matched it
|
401
|
+
expanded_files[File.expand_path(file_path)] = pattern
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
if expanded_files.any?
|
407
|
+
say "Found #{expanded_files.size} files to ignore from patterns"
|
408
|
+
end
|
409
|
+
|
410
|
+
expanded_files
|
411
|
+
rescue StandardError => e
|
412
|
+
say "Warning: Error processing ignore files YAML #{ignore_files_option}: #{e.message}"
|
413
|
+
{}
|
414
|
+
end
|
415
|
+
end
|
346
416
|
end
|
347
417
|
end
|
348
418
|
end
|
data/lib/expressir/coverage.rb
CHANGED
@@ -59,14 +59,16 @@ module Expressir
|
|
59
59
|
# Represents a documentation coverage report for EXPRESS schemas
|
60
60
|
class Report
|
61
61
|
attr_reader :repository, :schema_reports, :total_entities, :documented_entities,
|
62
|
-
:undocumented_entities
|
62
|
+
:undocumented_entities, :ignored_files
|
63
63
|
|
64
64
|
# Initialize a coverage report
|
65
65
|
# @param repository [Expressir::Model::Repository] The repository to analyze
|
66
66
|
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
67
|
-
|
67
|
+
# @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
|
68
|
+
def initialize(repository, skip_types = [], ignored_files = {})
|
68
69
|
@repository = repository
|
69
70
|
@skip_types = skip_types
|
71
|
+
@ignored_files = ignored_files
|
70
72
|
@schema_reports = []
|
71
73
|
@total_entities = []
|
72
74
|
@documented_entities = []
|
@@ -78,18 +80,20 @@ module Expressir
|
|
78
80
|
# Create a report from a repository
|
79
81
|
# @param repository [Expressir::Model::Repository] The repository to analyze
|
80
82
|
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
83
|
+
# @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
|
81
84
|
# @return [Report] The coverage report
|
82
|
-
def self.from_repository(repository, skip_types = [])
|
83
|
-
new(repository, skip_types)
|
85
|
+
def self.from_repository(repository, skip_types = [], ignored_files = {})
|
86
|
+
new(repository, skip_types, ignored_files)
|
84
87
|
end
|
85
88
|
|
86
89
|
# Create a report from a schema file
|
87
90
|
# @param path [String] Path to the schema file
|
88
91
|
# @param skip_types [Array<String>] Array of entity type names to skip from coverage
|
92
|
+
# @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
|
89
93
|
# @return [Report] The coverage report
|
90
|
-
def self.from_file(path, skip_types = [])
|
94
|
+
def self.from_file(path, skip_types = [], ignored_files = {})
|
91
95
|
repository = Expressir::Express::Parser.from_file(path)
|
92
|
-
new(repository, skip_types)
|
96
|
+
new(repository, skip_types, ignored_files)
|
93
97
|
end
|
94
98
|
|
95
99
|
# Calculate the overall coverage percentage
|
@@ -113,6 +117,39 @@ module Expressir
|
|
113
117
|
absolute_path
|
114
118
|
end
|
115
119
|
|
120
|
+
file_report = {
|
121
|
+
"file" => relative_path,
|
122
|
+
"file_basename" => File.basename(absolute_path),
|
123
|
+
"directory" => File.dirname(absolute_path),
|
124
|
+
"total" => report[:total].size,
|
125
|
+
"documented" => report[:documented].size,
|
126
|
+
"undocumented" => report[:undocumented],
|
127
|
+
"coverage" => report[:coverage],
|
128
|
+
"ignored" => report[:ignored] || false,
|
129
|
+
}
|
130
|
+
|
131
|
+
# Add matched pattern for ignored files
|
132
|
+
if report[:ignored] && report[:matched_pattern]
|
133
|
+
file_report["matched_pattern"] = report[:matched_pattern]
|
134
|
+
end
|
135
|
+
|
136
|
+
file_report
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Get ignored file reports
|
141
|
+
# @return [Array<Hash>] Array of ignored file report hashes
|
142
|
+
def ignored_file_reports
|
143
|
+
@schema_reports.select { |report| report[:ignored] }.map do |report|
|
144
|
+
absolute_path = report[:schema].file
|
145
|
+
relative_path = begin
|
146
|
+
Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
|
147
|
+
rescue ArgumentError
|
148
|
+
# If paths are on different drives or otherwise incompatible,
|
149
|
+
# fall back to the absolute path
|
150
|
+
absolute_path
|
151
|
+
end
|
152
|
+
|
116
153
|
{
|
117
154
|
"file" => relative_path,
|
118
155
|
"file_basename" => File.basename(absolute_path),
|
@@ -121,6 +158,7 @@ module Expressir
|
|
121
158
|
"documented" => report[:documented].size,
|
122
159
|
"undocumented" => report[:undocumented],
|
123
160
|
"coverage" => report[:coverage],
|
161
|
+
"matched_pattern" => report[:matched_pattern],
|
124
162
|
}
|
125
163
|
end
|
126
164
|
end
|
@@ -182,11 +220,14 @@ module Expressir
|
|
182
220
|
schema_report = process_schema(schema)
|
183
221
|
@schema_reports << schema_report
|
184
222
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
223
|
+
# Only include non-ignored files in overall statistics
|
224
|
+
unless schema_report[:ignored]
|
225
|
+
@total_entities.concat(schema_report[:total])
|
226
|
+
@documented_entities.concat(schema_report[:documented])
|
227
|
+
@undocumented_entities.concat(schema_report[:undocumented].map do |entity|
|
228
|
+
{ schema: schema.id, entity: entity }
|
229
|
+
end)
|
230
|
+
end
|
190
231
|
end
|
191
232
|
end
|
192
233
|
|
@@ -195,17 +236,26 @@ module Expressir
|
|
195
236
|
# @return [Hash] A hash with coverage information
|
196
237
|
def process_schema(schema)
|
197
238
|
entities = Expressir::Coverage.find_entities(schema, @skip_types)
|
198
|
-
documented = entities.select
|
239
|
+
documented = entities.select do |e|
|
240
|
+
Expressir::Coverage.entity_documented?(e)
|
241
|
+
end
|
199
242
|
undocumented = entities - documented
|
200
243
|
|
201
244
|
coverage = entities.empty? ? 100.0 : (documented.size.to_f / entities.size) * 100
|
202
245
|
|
246
|
+
# Check if this schema file is ignored
|
247
|
+
schema_file = File.expand_path(schema.file) if schema.file
|
248
|
+
ignored = @ignored_files.key?(schema_file)
|
249
|
+
matched_pattern = @ignored_files[schema_file] if ignored
|
250
|
+
|
203
251
|
{
|
204
252
|
schema: schema,
|
205
253
|
total: entities,
|
206
254
|
documented: documented,
|
207
255
|
undocumented: undocumented.map { |e| format_entity(e) },
|
208
256
|
coverage: coverage,
|
257
|
+
ignored: ignored,
|
258
|
+
matched_pattern: matched_pattern,
|
209
259
|
}
|
210
260
|
end
|
211
261
|
|
@@ -352,10 +402,18 @@ module Expressir
|
|
352
402
|
entities.concat(container.subtype_constraints) if container.subtype_constraints
|
353
403
|
|
354
404
|
# Recursively find nested entities in nested containers
|
355
|
-
container.types&.each
|
356
|
-
|
357
|
-
|
358
|
-
container.
|
405
|
+
container.types&.each do |type|
|
406
|
+
entities.concat(find_nested_entities(type))
|
407
|
+
end
|
408
|
+
container.entities&.each do |entity|
|
409
|
+
entities.concat(find_nested_entities(entity))
|
410
|
+
end
|
411
|
+
container.functions&.each do |function|
|
412
|
+
entities.concat(find_nested_entities(function))
|
413
|
+
end
|
414
|
+
container.procedures&.each do |procedure|
|
415
|
+
entities.concat(find_nested_entities(procedure))
|
416
|
+
end
|
359
417
|
|
360
418
|
when Expressir::Model::Declarations::Rule
|
361
419
|
# Rule nested entities
|
@@ -368,10 +426,18 @@ module Expressir
|
|
368
426
|
entities.concat(container.subtype_constraints) if container.subtype_constraints
|
369
427
|
|
370
428
|
# Recursively find nested entities in nested containers
|
371
|
-
container.types&.each
|
372
|
-
|
373
|
-
|
374
|
-
container.
|
429
|
+
container.types&.each do |type|
|
430
|
+
entities.concat(find_nested_entities(type))
|
431
|
+
end
|
432
|
+
container.entities&.each do |entity|
|
433
|
+
entities.concat(find_nested_entities(entity))
|
434
|
+
end
|
435
|
+
container.functions&.each do |function|
|
436
|
+
entities.concat(find_nested_entities(function))
|
437
|
+
end
|
438
|
+
container.procedures&.each do |procedure|
|
439
|
+
entities.concat(find_nested_entities(procedure))
|
440
|
+
end
|
375
441
|
|
376
442
|
when Expressir::Model::Declarations::Procedure
|
377
443
|
# Procedure nested entities
|
@@ -385,10 +451,18 @@ module Expressir
|
|
385
451
|
entities.concat(container.subtype_constraints) if container.subtype_constraints
|
386
452
|
|
387
453
|
# Recursively find nested entities in nested containers
|
388
|
-
container.types&.each
|
389
|
-
|
390
|
-
|
391
|
-
container.
|
454
|
+
container.types&.each do |type|
|
455
|
+
entities.concat(find_nested_entities(type))
|
456
|
+
end
|
457
|
+
container.entities&.each do |entity|
|
458
|
+
entities.concat(find_nested_entities(entity))
|
459
|
+
end
|
460
|
+
container.functions&.each do |function|
|
461
|
+
entities.concat(find_nested_entities(function))
|
462
|
+
end
|
463
|
+
container.procedures&.each do |procedure|
|
464
|
+
entities.concat(find_nested_entities(procedure))
|
465
|
+
end
|
392
466
|
|
393
467
|
when Expressir::Model::Declarations::Interface
|
394
468
|
# Interface nested entities
|
@@ -450,7 +524,7 @@ module Expressir
|
|
450
524
|
type_subtype_skips.include?(entity_subtype)
|
451
525
|
# Check FUNCTION:INNER exclusions
|
452
526
|
elsif entity_class == "Expressir::Model::Declarations::Function" && function_subtype_skips.include?("INNER")
|
453
|
-
|
527
|
+
inner_function?(entity)
|
454
528
|
else
|
455
529
|
false
|
456
530
|
end
|
@@ -480,7 +554,7 @@ module Expressir
|
|
480
554
|
# Check if a function is an inner function (nested within another function, rule, or procedure)
|
481
555
|
# @param function_entity [Expressir::Model::Declarations::Function] The function entity to check
|
482
556
|
# @return [Boolean] True if the function is nested within another function, rule, or procedure
|
483
|
-
def self.
|
557
|
+
def self.inner_function?(function_entity)
|
484
558
|
return false unless function_entity.respond_to?(:parent) && function_entity.parent
|
485
559
|
|
486
560
|
# Check if the parent is a function, rule, or procedure (not a schema)
|
@@ -9,7 +9,8 @@ module Expressir
|
|
9
9
|
# @param root_path [String] Express repository root path, to be stripped from Express file paths to create a portable cache file
|
10
10
|
# @param test_overwrite_version [String] don't use, only for tests
|
11
11
|
# @return [nil]
|
12
|
-
def self.to_file(file, content, root_path: nil,
|
12
|
+
def self.to_file(file, content, root_path: nil,
|
13
|
+
test_overwrite_version: nil)
|
13
14
|
version = test_overwrite_version || VERSION
|
14
15
|
|
15
16
|
cache = Model::Cache.new(
|