suma 0.2.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +7 -1
- data/.rubocop_todo.yml +170 -13
- data/CLAUDE.md +37 -11
- data/Gemfile +3 -3
- data/README.adoc +127 -13
- data/exe/suma +1 -1
- data/lib/suma/cli/build.rb +0 -5
- data/lib/suma/cli/check_svg_quality.rb +82 -97
- data/lib/suma/cli/compare.rb +0 -1
- data/lib/suma/cli/convert_jsdai.rb +0 -2
- data/lib/suma/cli/core.rb +119 -0
- data/lib/suma/cli/export.rb +37 -22
- data/lib/suma/cli/extract_terms.rb +5 -8
- data/lib/suma/cli/generate_register.rb +34 -0
- data/lib/suma/cli/generate_schemas.rb +0 -2
- data/lib/suma/cli/reformat.rb +39 -64
- data/lib/suma/cli/validate.rb +14 -7
- data/lib/suma/cli/validate_links.rb +11 -157
- data/lib/suma/cli.rb +12 -141
- data/lib/suma/collection_config.rb +0 -2
- data/lib/suma/collection_manifest.rb +7 -110
- data/lib/suma/eengine/wrapper.rb +0 -1
- data/lib/suma/eengine.rb +8 -0
- data/lib/suma/express_reformatter.rb +94 -0
- data/lib/suma/express_schema.rb +0 -1
- data/lib/suma/jsdai/figure.rb +0 -3
- data/lib/suma/jsdai.rb +5 -2
- data/lib/suma/link_validation.rb +144 -0
- data/lib/suma/link_validator.rb +17 -8
- data/lib/suma/manifest_traverser.rb +92 -0
- data/lib/suma/processor.rb +5 -8
- data/lib/suma/register_manifest_generator.rb +163 -0
- data/lib/suma/schema_category.rb +83 -0
- data/lib/suma/schema_collection.rb +30 -34
- data/lib/suma/schema_comparer.rb +4 -3
- data/lib/suma/schema_compiler.rb +86 -0
- data/lib/suma/schema_discovery.rb +75 -0
- data/lib/suma/schema_exporter.rb +14 -42
- data/lib/suma/schema_manifest_generator.rb +14 -6
- data/lib/suma/schema_naming.rb +111 -0
- data/lib/suma/schema_template/document.rb +141 -0
- data/lib/suma/schema_template/plain.rb +46 -0
- data/lib/suma/schema_template.rb +19 -0
- data/lib/suma/svg_quality/batch_report.rb +0 -2
- data/lib/suma/svg_quality/formatters.rb +12 -0
- data/lib/suma/svg_quality/scanner.rb +61 -0
- data/lib/suma/svg_quality.rb +4 -1
- data/lib/suma/term_classification.rb +78 -0
- data/lib/suma/term_extractor.rb +125 -81
- data/lib/suma/urn.rb +61 -0
- data/lib/suma/version.rb +1 -1
- data/lib/suma.rb +35 -3
- data/suma.gemspec +1 -1
- metadata +28 -6
- data/lib/suma/schema_attachment.rb +0 -103
- data/lib/suma/schema_document.rb +0 -118
data/lib/suma/cli/reformat.rb
CHANGED
|
@@ -1,88 +1,63 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
-
require_relative "../thor_ext"
|
|
5
4
|
|
|
6
5
|
module Suma
|
|
7
6
|
module Cli
|
|
8
|
-
# Reformat command for reformatting EXPRESS files
|
|
7
|
+
# Reformat command for reformatting EXPRESS files.
|
|
8
|
+
#
|
|
9
|
+
# Thin Thor adapter around Suma::ExpressReformatter: argument
|
|
10
|
+
# parsing, file discovery, and read/transform/write. The content
|
|
11
|
+
# transformation itself lives in the deep module and is tested
|
|
12
|
+
# independently.
|
|
9
13
|
class Reformat < Thor
|
|
10
|
-
desc "reformat EXPRESS_FILE_PATH",
|
|
11
|
-
"Reformat EXPRESS files"
|
|
14
|
+
desc "reformat EXPRESS_FILE_PATH", "Reformat EXPRESS files"
|
|
12
15
|
option :recursive, type: :boolean, default: false, aliases: "-r",
|
|
13
16
|
desc: "Reformat EXPRESS files under the specified " \
|
|
14
17
|
"path recursively"
|
|
15
18
|
|
|
16
|
-
def reformat(express_file_path)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"`#{express_file_path}` not found."
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
if File.extname(express_file_path) != ".exp"
|
|
24
|
-
raise ArgumentError, "Specified file `#{express_file_path}` is " \
|
|
25
|
-
"not an EXPRESS file."
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
exp_files = [express_file_path]
|
|
29
|
-
elsif options[:recursive]
|
|
30
|
-
exp_files = Dir.glob("#{express_file_path}/**/*.exp")
|
|
31
|
-
else
|
|
32
|
-
exp_files = Dir.glob("#{express_file_path}/*.exp")
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
if exp_files.empty?
|
|
36
|
-
raise Errno::ENOENT, "No EXPRESS files found in " \
|
|
37
|
-
"`#{express_file_path}`."
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
run(exp_files)
|
|
19
|
+
def reformat(express_file_path)
|
|
20
|
+
files = discover_files(express_file_path)
|
|
21
|
+
ensure_files_found!(files, express_file_path)
|
|
22
|
+
process_files(files)
|
|
41
23
|
end
|
|
42
24
|
|
|
43
25
|
private
|
|
44
26
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
end
|
|
27
|
+
def discover_files(path)
|
|
28
|
+
return [path] if File.file?(path)
|
|
29
|
+
return Dir.glob("#{path}/**/*.exp") if options[:recursive]
|
|
50
30
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
file_content = File.read(file)
|
|
54
|
-
|
|
55
|
-
# Extract all comments between '(*"' and '\n*)'
|
|
56
|
-
# Avoid incorrect selection of some comment blocks
|
|
57
|
-
# containing '(*text*)' inside
|
|
58
|
-
comments = file_content.scan(/\(\*"(.*?)\n\*\)/m).map(&:first)
|
|
59
|
-
|
|
60
|
-
if comments.any?
|
|
61
|
-
content_without_comments = file_content.gsub(/\(\*".*?\n\*\)/m, "")
|
|
62
|
-
|
|
63
|
-
# remove extra newlines
|
|
64
|
-
new_content = content_without_comments.gsub(/(\n\n+)/, "\n\n")
|
|
65
|
-
# Add '(*"' and '\n*)' to enclose the comment block
|
|
66
|
-
new_comments = comments.map { |c| "(*\"#{c}\n*)" }.join("\n\n")
|
|
67
|
-
# Append the comments to the end of the file
|
|
68
|
-
new_content = "#{new_content}\n\n#{new_comments}\n"
|
|
31
|
+
Dir.glob("#{path}/*.exp")
|
|
32
|
+
end
|
|
69
33
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
34
|
+
def ensure_files_found!(files, path)
|
|
35
|
+
if File.file?(path)
|
|
36
|
+
unless File.extname(path) == ".exp"
|
|
37
|
+
raise ArgumentError,
|
|
38
|
+
"Specified file `#{path}` is not an EXPRESS file."
|
|
76
39
|
end
|
|
77
|
-
|
|
78
|
-
|
|
40
|
+
elsif !File.exist?(path)
|
|
41
|
+
raise Errno::ENOENT,
|
|
42
|
+
"Specified EXPRESS file `#{path}` not found."
|
|
79
43
|
end
|
|
44
|
+
return unless files.empty?
|
|
45
|
+
|
|
46
|
+
raise Errno::ENOENT, "No EXPRESS files found in `#{path}`."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def process_files(files)
|
|
50
|
+
files.each { |file| reformat_one(file) }
|
|
80
51
|
end
|
|
81
52
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
53
|
+
def reformat_one(file)
|
|
54
|
+
result = ExpressReformatter.call(File.read(file))
|
|
55
|
+
if result.changed?
|
|
56
|
+
File.write(file, result.content)
|
|
57
|
+
puts "Reformatted EXPRESS file and saved to #{file}"
|
|
58
|
+
else
|
|
59
|
+
puts "No changes made to #{file}"
|
|
60
|
+
end
|
|
86
61
|
end
|
|
87
62
|
end
|
|
88
63
|
end
|
data/lib/suma/cli/validate.rb
CHANGED
|
@@ -4,16 +4,23 @@ require "thor"
|
|
|
4
4
|
|
|
5
5
|
module Suma
|
|
6
6
|
module Cli
|
|
7
|
-
#
|
|
7
|
+
# Validate command group. Thin Thor adapter around
|
|
8
|
+
# +Suma::LinkValidation+ — argument parsing, result presentation.
|
|
9
|
+
# All orchestration (manifest loading, link extraction, schema
|
|
10
|
+
# indexing, validation) lives in the deep module and is reachable
|
|
11
|
+
# from specs without invoking Thor.
|
|
8
12
|
class Validate < Thor
|
|
9
13
|
desc "links SCHEMAS_FILE DOCUMENTS_PATH [OUTPUT_FILE]",
|
|
10
14
|
"Extract and validate express links without creating intermediate file"
|
|
11
|
-
def links(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
def links(schemas_file = "schemas-srl.yml",
|
|
16
|
+
documents_path = "documents",
|
|
17
|
+
output_file = "validation_results.txt")
|
|
18
|
+
result = LinkValidation.new(
|
|
19
|
+
schemas_file: schemas_file,
|
|
20
|
+
documents_path: documents_path,
|
|
21
|
+
output_file: output_file,
|
|
22
|
+
).call
|
|
23
|
+
puts LinkValidation.generate_summary(result)
|
|
17
24
|
end
|
|
18
25
|
end
|
|
19
26
|
end
|
|
@@ -1,170 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
-
require_relative "../utils"
|
|
5
|
-
require_relative "../link_validator"
|
|
6
|
-
require "expressir"
|
|
7
4
|
|
|
8
5
|
module Suma
|
|
9
6
|
module Cli
|
|
7
|
+
# Deprecated: prefer +Cli::Validate#links+ (the +suma validate links+
|
|
8
|
+
# subcommand). This class is retained as a backwards-compat entry
|
|
9
|
+
# point — all orchestration now lives in +Suma::LinkValidation+.
|
|
10
10
|
class ValidateLinks < Thor
|
|
11
11
|
desc "extract_and_validate SCHEMAS_FILE DOCUMENTS_PATH [OUTPUT_FILE]",
|
|
12
12
|
"Extract and validate express links without creating intermediate file"
|
|
13
13
|
def extract_and_validate(schemas_file = "schemas-srl.yml",
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
all_files = adoc_files + exp_files
|
|
24
|
-
display_file_counts(adoc_files, exp_files)
|
|
25
|
-
|
|
26
|
-
links_by_file = extract_links(all_files)
|
|
27
|
-
|
|
28
|
-
repo = load_express_schemas(schemas_config)
|
|
29
|
-
index = SchemaIndex.new(repo)
|
|
30
|
-
unresolved = LinkValidator.new(index).validate(links_by_file)
|
|
31
|
-
|
|
32
|
-
write_validation_results(paths[:output_file], paths[:output_file_rel],
|
|
33
|
-
unresolved, links_by_file)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
def load_dependencies
|
|
39
|
-
require "expressir"
|
|
40
|
-
require "ruby-progressbar"
|
|
41
|
-
require "pathname"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def prepare_file_paths(schemas_file, documents_path, output_file)
|
|
45
|
-
schemas_file_path = Pathname.new(schemas_file).expand_path
|
|
46
|
-
documents_path_exp = Pathname.new(documents_path).expand_path
|
|
47
|
-
output_file_path = Pathname.new(output_file).expand_path
|
|
48
|
-
|
|
49
|
-
schemas_file_rel = Pathname.new(schemas_file_path).relative_path_from(Pathname.pwd).to_s
|
|
50
|
-
documents_path_rel = Pathname.new(documents_path_exp).relative_path_from(Pathname.pwd).to_s
|
|
51
|
-
output_file_rel = Pathname.new(output_file_path).relative_path_from(Pathname.pwd).to_s
|
|
52
|
-
|
|
53
|
-
puts "Extracting and validating express links using schemas from #{schemas_file_rel}..."
|
|
54
|
-
puts "Looking for documents in #{documents_path_rel}..."
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
schemas_file: schemas_file_path,
|
|
58
|
-
schemas_file_rel: schemas_file_rel,
|
|
59
|
-
documents_path: documents_path_exp,
|
|
60
|
-
documents_path_rel: documents_path_rel,
|
|
61
|
-
output_file: output_file_path,
|
|
62
|
-
output_file_rel: output_file_rel,
|
|
63
|
-
}
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def load_schemas_config(schemas_file_path)
|
|
67
|
-
schemas_config = Expressir::SchemaManifest.from_yaml(File.read(schemas_file_path))
|
|
68
|
-
schemas_config.set_initial_path(schemas_file_path.to_s)
|
|
69
|
-
schemas_config
|
|
70
|
-
rescue StandardError => e
|
|
71
|
-
raise Suma::Error, "Error loading schemas file: #{e.message}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def collect_schema_paths(schemas_config, schemas_file_rel)
|
|
75
|
-
exp_files = schemas_config.schemas.filter_map(&:path)
|
|
76
|
-
puts "Found #{exp_files.size} EXPRESS schema files from #{schemas_file_rel}"
|
|
77
|
-
exp_files
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def find_adoc_files(documents_path)
|
|
81
|
-
Dir.glob(documents_path.join("**", "*.adoc").to_s)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def display_file_counts(adoc_files, exp_files)
|
|
85
|
-
puts "Found #{adoc_files.size} AsciiDoc files and #{exp_files.size} EXPRESS files"
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def create_progress_bar(title, total)
|
|
89
|
-
ProgressBar.create(
|
|
90
|
-
title: title,
|
|
91
|
-
total: total,
|
|
92
|
-
format: "%t: [%B] %p%% %c/%C %e",
|
|
93
|
-
progress_mark: "=",
|
|
94
|
-
remainder_mark: " ",
|
|
95
|
-
length: 80,
|
|
96
|
-
)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def extract_links(files)
|
|
100
|
-
links_by_file = {}
|
|
101
|
-
link_count = 0
|
|
102
|
-
|
|
103
|
-
progress = create_progress_bar("Processing files", files.size)
|
|
104
|
-
|
|
105
|
-
files.each do |file|
|
|
106
|
-
progress.increment
|
|
107
|
-
begin
|
|
108
|
-
content = File.read(file)
|
|
109
|
-
express_links = content.scan(/<<express:([^,>]+)(?:,[^>]+)?>>/).flatten.uniq
|
|
110
|
-
|
|
111
|
-
if express_links.any?
|
|
112
|
-
links_by_file[file] = express_links
|
|
113
|
-
link_count += express_links.size
|
|
114
|
-
end
|
|
115
|
-
rescue StandardError => e
|
|
116
|
-
puts "\nWarning: Could not read file #{file}: #{e.message}"
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
puts "\nExtracted #{link_count} unique express links from #{links_by_file.size} files"
|
|
121
|
-
links_by_file
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def load_express_schemas(schemas_config)
|
|
125
|
-
schema_paths = {}
|
|
126
|
-
schemas_config.schemas.each { |s| schema_paths[s.id] = s.path }
|
|
127
|
-
|
|
128
|
-
puts "Loading #{schema_paths.size} EXPRESS schemas for validation..."
|
|
129
|
-
|
|
130
|
-
loading_progress = create_progress_bar("Loading schemas", schema_paths.size)
|
|
131
|
-
|
|
132
|
-
begin
|
|
133
|
-
repo = Expressir::Express::Parser.from_files(schema_paths.values) do |filename, _schemas, error|
|
|
134
|
-
loading_progress.increment
|
|
135
|
-
puts "\nWarning: Error loading schema #{filename}: #{error.message}" if error
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
puts "Successfully loaded #{repo.schemas.size} schemas"
|
|
139
|
-
repo
|
|
140
|
-
rescue StandardError => e
|
|
141
|
-
raise Suma::Error, "Error loading schemas: #{e.message}"
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def write_validation_results(output_file_path, output_file_rel,
|
|
146
|
-
unresolved_links, links_by_file)
|
|
147
|
-
total_links = links_by_file.values.sum(&:size)
|
|
148
|
-
|
|
149
|
-
results = []
|
|
150
|
-
results << "Validation complete. Checked #{total_links} links."
|
|
151
|
-
|
|
152
|
-
if unresolved_links.empty?
|
|
153
|
-
results << "✅ All links resolved successfully!"
|
|
154
|
-
else
|
|
155
|
-
results << "❌ Found #{unresolved_links.size} unresolved links:"
|
|
156
|
-
unresolved_links.each do |issue|
|
|
157
|
-
results << "#{issue.file}:#{issue.line} - <<express:#{issue.link}>> - #{issue.reason}"
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
begin
|
|
162
|
-
File.write(output_file_path, results.join("\n"))
|
|
163
|
-
puts "Validation results written to #{output_file_rel}"
|
|
164
|
-
rescue StandardError => e
|
|
165
|
-
puts "Error writing to output file: #{e.message}"
|
|
166
|
-
puts results
|
|
167
|
-
end
|
|
14
|
+
documents_path = "documents",
|
|
15
|
+
output_file = "validation_results.txt")
|
|
16
|
+
result = LinkValidation.new(
|
|
17
|
+
schemas_file: schemas_file,
|
|
18
|
+
documents_path: documents_path,
|
|
19
|
+
output_file: output_file,
|
|
20
|
+
).call
|
|
21
|
+
puts LinkValidation.generate_summary(result)
|
|
168
22
|
end
|
|
169
23
|
end
|
|
170
24
|
end
|
data/lib/suma/cli.rb
CHANGED
|
@@ -1,147 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "thor"
|
|
4
|
-
require_relative "thor_ext"
|
|
5
|
-
require_relative "cli/validate"
|
|
6
|
-
require_relative "cli/check_svg_quality"
|
|
7
|
-
require "expressir"
|
|
8
|
-
require "expressir/cli"
|
|
9
|
-
|
|
10
3
|
module Suma
|
|
11
4
|
module Cli
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
require_relative "cli/build"
|
|
25
|
-
Cli::Build.start
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
desc "generate-schemas METANORMA_MANIFEST_FILE SCHEMA_MANIFEST_FILE",
|
|
29
|
-
"Generate EXPRESS schema manifest file from Metanorma site manifest"
|
|
30
|
-
option :exclude_paths, type: :string, default: nil, aliases: "-e",
|
|
31
|
-
desc: "Exclude schemas paths by pattern " \
|
|
32
|
-
"(e.g. `*_lf.exp`)"
|
|
33
|
-
def generate_schemas(_metanorma_manifest_file, _schema_manifest_file)
|
|
34
|
-
require_relative "cli/generate_schemas"
|
|
35
|
-
Cli::GenerateSchemas.start
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
desc "reformat EXPRESS_FILE_PATH",
|
|
39
|
-
"Reformat EXPRESS files"
|
|
40
|
-
option :recursive, type: :boolean, default: false, aliases: "-r",
|
|
41
|
-
desc: "Reformat EXPRESS files under the specified " \
|
|
42
|
-
"path recursively"
|
|
43
|
-
def reformat(_express_file_path)
|
|
44
|
-
require_relative "cli/reformat"
|
|
45
|
-
Cli::Reformat.start
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
desc "extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH",
|
|
49
|
-
"Extract terms from SCHEMA_MANIFEST_FILE into " \
|
|
50
|
-
"Glossarist v2 format"
|
|
51
|
-
option :language_code, type: :string, default: "eng", aliases: "-l",
|
|
52
|
-
desc: "Language code for the Glossarist"
|
|
53
|
-
def extract_terms(_schema_manifest_file, _glossarist_output_path)
|
|
54
|
-
require_relative "cli/extract_terms"
|
|
55
|
-
Cli::ExtractTerms.start
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
desc "convert-jsdai XML_FILE IMAGE_FILE OUTPUT_DIR",
|
|
59
|
-
"Convert JSDAI XML and image files to SVG and EXP files"
|
|
60
|
-
def convert_jsdai(_xml_file, _image_file, _output_dir)
|
|
61
|
-
require_relative "cli/convert_jsdai"
|
|
62
|
-
Cli::ConvertJsdai.start
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
desc "export *FILES",
|
|
66
|
-
"Export EXPRESS schemas from manifest files or " \
|
|
67
|
-
"standalone EXPRESS files"
|
|
68
|
-
option :output, type: :string, aliases: "-o", required: true,
|
|
69
|
-
desc: "Output directory path"
|
|
70
|
-
option :annotations, type: :boolean, default: false,
|
|
71
|
-
desc: "Include annotations (remarks/comments)"
|
|
72
|
-
option :zip, type: :boolean, default: false,
|
|
73
|
-
desc: "Create ZIP archive of exported schemas"
|
|
74
|
-
def export(*_files)
|
|
75
|
-
require_relative "cli/export"
|
|
76
|
-
Cli::Export.start
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
desc "compare TRIAL_SCHEMA REFERENCE_SCHEMA",
|
|
80
|
-
"Compare EXPRESS schemas using eengine and generate Change YAML"
|
|
81
|
-
option :output, type: :string, aliases: "-o",
|
|
82
|
-
desc: "Output Change YAML file path"
|
|
83
|
-
option :version, type: :string, aliases: "-v", required: true,
|
|
84
|
-
desc: "Version number for this change version"
|
|
85
|
-
option :mode, type: :string, default: "resource",
|
|
86
|
-
desc: "Schema comparison mode (resource/module)"
|
|
87
|
-
option :trial_stepmod, type: :string,
|
|
88
|
-
desc: "Override auto-detected trial repo root"
|
|
89
|
-
option :reference_stepmod, type: :string,
|
|
90
|
-
desc: "Override auto-detected reference repo root"
|
|
91
|
-
option :verbose, type: :boolean, default: false,
|
|
92
|
-
desc: "Enable verbose output"
|
|
93
|
-
def compare(_trial_schema, _reference_schema)
|
|
94
|
-
require_relative "cli/compare"
|
|
95
|
-
Cli::Compare.start
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
desc "validate SUBCOMMAND ...ARGS", "Validate express documents"
|
|
99
|
-
subcommand "validate", Cli::Validate
|
|
100
|
-
|
|
101
|
-
desc "check_svg_quality [PATH]",
|
|
102
|
-
"Check SVG quality and sort by severity (critical files first)"
|
|
103
|
-
option :pattern, type: :string, default: Cli::CheckSvgQuality::DEFAULT_PATTERN,
|
|
104
|
-
desc: "Glob pattern for finding SVG files"
|
|
105
|
-
option :profile, type: :string,
|
|
106
|
-
default: Cli::CheckSvgQuality::DEFAULT_PROFILE,
|
|
107
|
-
desc: "Validation profile to use (metanorma, svg_1_2_rfc, etc.)"
|
|
108
|
-
option :format, type: :string, default: "terminal",
|
|
109
|
-
desc: "Output format: terminal, yaml, json"
|
|
110
|
-
option :output, type: :string, aliases: "-o",
|
|
111
|
-
desc: "Output file path"
|
|
112
|
-
option :min_errors, type: :numeric,
|
|
113
|
-
desc: "Minimum error count threshold"
|
|
114
|
-
option :limit, type: :numeric, default: nil,
|
|
115
|
-
desc: "Maximum number of files to show (default: unlimited)"
|
|
116
|
-
option :sort, type: :string, default: "errors",
|
|
117
|
-
desc: "Sort by: errors (most errors first) or quality (lowest scores first)"
|
|
118
|
-
option :progress, type: :boolean, default: false,
|
|
119
|
-
desc: "Show progress during processing"
|
|
120
|
-
option :summary_only, type: :boolean, default: false,
|
|
121
|
-
desc: "Show only summary"
|
|
122
|
-
def check_svg_quality(path = Cli::CheckSvgQuality::DATA_PATH)
|
|
123
|
-
require_relative "cli/check_svg_quality"
|
|
124
|
-
|
|
125
|
-
analyzer = Cli::CheckSvgQuality.new(
|
|
126
|
-
pattern: options[:pattern],
|
|
127
|
-
profile: options[:profile],
|
|
128
|
-
format: options[:format],
|
|
129
|
-
output: options[:output],
|
|
130
|
-
min_errors: options[:min_errors],
|
|
131
|
-
summary_only: options[:summary_only],
|
|
132
|
-
progress: options[:progress],
|
|
133
|
-
limit: options[:limit],
|
|
134
|
-
sort: options[:sort],
|
|
135
|
-
)
|
|
136
|
-
analyzer.run(path)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
desc "expressir SUBCOMMAND ...ARGS", "Expressir commands"
|
|
140
|
-
subcommand "expressir", Expressir::Cli
|
|
141
|
-
|
|
142
|
-
def self.exit_on_failure?
|
|
143
|
-
true
|
|
144
|
-
end
|
|
145
|
-
end
|
|
5
|
+
autoload :Core, "suma/cli/core"
|
|
6
|
+
autoload :Build, "suma/cli/build"
|
|
7
|
+
autoload :CheckSvgQuality, "suma/cli/check_svg_quality"
|
|
8
|
+
autoload :Compare, "suma/cli/compare"
|
|
9
|
+
autoload :ConvertJsdai, "suma/cli/convert_jsdai"
|
|
10
|
+
autoload :Export, "suma/cli/export"
|
|
11
|
+
autoload :ExtractTerms, "suma/cli/extract_terms"
|
|
12
|
+
autoload :GenerateRegister, "suma/cli/generate_register"
|
|
13
|
+
autoload :GenerateSchemas, "suma/cli/generate_schemas"
|
|
14
|
+
autoload :Reformat, "suma/cli/reformat"
|
|
15
|
+
autoload :Validate, "suma/cli/validate"
|
|
16
|
+
autoload :ValidateLinks, "suma/cli/validate_links"
|
|
146
17
|
end
|
|
147
18
|
end
|
|
@@ -4,6 +4,13 @@ require "metanorma"
|
|
|
4
4
|
require "expressir"
|
|
5
5
|
|
|
6
6
|
module Suma
|
|
7
|
+
# Pure data model for one node of a Metanorma collection manifest.
|
|
8
|
+
#
|
|
9
|
+
# CollectionManifest extends the Metanorma config manifest to add the
|
|
10
|
+
# `schemas_only` flag and an `entry` sub-collection. It owns only state:
|
|
11
|
+
# attributes, YAML mappings, and the `schema_config` slot populated by
|
|
12
|
+
# SchemaDiscovery. Tree-walking logic lives in ManifestTraverser; schema
|
|
13
|
+
# I/O lives in SchemaDiscovery.
|
|
7
14
|
class CollectionManifest < Metanorma::Collection::Config::Manifest
|
|
8
15
|
attribute :schemas_only, Lutaml::Model::Type::Boolean
|
|
9
16
|
attribute :entry, CollectionManifest, collection: true,
|
|
@@ -32,115 +39,5 @@ module Suma
|
|
|
32
39
|
def docref_from_yaml(model, value)
|
|
33
40
|
model.entry = CollectionManifest.from_yaml(value.to_yaml)
|
|
34
41
|
end
|
|
35
|
-
|
|
36
|
-
# Recursively exports schema configuration by traversing collection manifests.
|
|
37
|
-
#
|
|
38
|
-
# This method builds an EXPRESS Schema Manifest (Expressir::SchemaManifest) by:
|
|
39
|
-
# 1. Starting with an empty or existing Expressir::SchemaManifest
|
|
40
|
-
# 2. Recursively traversing child entries to collect schemas
|
|
41
|
-
# 3. Using Expressir::SchemaManifest#concat to combine manifests
|
|
42
|
-
#
|
|
43
|
-
# The actual schema manifest operations (creation, concatenation, serialization)
|
|
44
|
-
# are handled by Expressir's SchemaManifest class, keeping the logic DRY.
|
|
45
|
-
#
|
|
46
|
-
# @param path [String] Base path for resolving relative schema paths
|
|
47
|
-
# @return [Expressir::SchemaManifest] Combined schema manifest
|
|
48
|
-
def export_schema_config(path)
|
|
49
|
-
export_config = @schema_config || Expressir::SchemaManifest.new
|
|
50
|
-
return export_config unless entry
|
|
51
|
-
|
|
52
|
-
entry.each do |x|
|
|
53
|
-
child_config = x.export_schema_config(path)
|
|
54
|
-
# Use Expressir's concat method to combine schema manifests
|
|
55
|
-
export_config.concat(child_config) if child_config
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
export_config
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def lookup_schemas_only
|
|
62
|
-
results = entry.select(&:schemas_only)
|
|
63
|
-
results << self if schemas_only
|
|
64
|
-
results
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def process_entry(schema_output_path)
|
|
68
|
-
return [self] unless entry
|
|
69
|
-
|
|
70
|
-
ret = entry.each_with_object([]) do |e, m|
|
|
71
|
-
add = e.expand_schemas_only(schema_output_path)
|
|
72
|
-
m.concat(add)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
self.entry = ret
|
|
76
|
-
[self]
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def expand_schemas_only(schema_output_path)
|
|
80
|
-
return process_entry(schema_output_path) unless file
|
|
81
|
-
|
|
82
|
-
update_schema_config
|
|
83
|
-
|
|
84
|
-
return process_entry(schema_output_path) unless schemas_only
|
|
85
|
-
|
|
86
|
-
# If we are going to keep the schemas-only file and compile it, we can't
|
|
87
|
-
# have it showing up in output.
|
|
88
|
-
self.index = false
|
|
89
|
-
|
|
90
|
-
[self, added_collection_manifest(schema_output_path)]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def remove_schemas_only_sources
|
|
94
|
-
ret = entry.each_with_object([]) do |e, m|
|
|
95
|
-
e.schemas_only or m << e
|
|
96
|
-
end
|
|
97
|
-
self.entry = ret
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def entries(schema_output_path)
|
|
101
|
-
@schema_config.schemas.map do |schema|
|
|
102
|
-
fname = [File.basename(schema.path, ".exp"), ".xml"].join
|
|
103
|
-
|
|
104
|
-
CollectionManifest.new(
|
|
105
|
-
identifier: schema.id,
|
|
106
|
-
title: schema.id,
|
|
107
|
-
file: File.join(schema_output_path, schema.id, "doc_#{fname}"),
|
|
108
|
-
# schema_source: schema.path
|
|
109
|
-
)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def added_collection_manifest(schema_output_path)
|
|
114
|
-
doc = CollectionConfig.from_file(file)
|
|
115
|
-
doc_id = doc.bibdata.id
|
|
116
|
-
|
|
117
|
-
# we need to separate this file from the following new entries
|
|
118
|
-
added = CollectionManifest.new(
|
|
119
|
-
title: "Collection",
|
|
120
|
-
type: "collection",
|
|
121
|
-
identifier: "#{identifier}_",
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
added.entry = [
|
|
125
|
-
CollectionManifest.new(
|
|
126
|
-
title: doc_id,
|
|
127
|
-
type: "document",
|
|
128
|
-
entry: entries(schema_output_path),
|
|
129
|
-
),
|
|
130
|
-
]
|
|
131
|
-
|
|
132
|
-
added
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def update_schema_config
|
|
136
|
-
# If there is collection.yml, this is a document collection, we process
|
|
137
|
-
# schemas.yaml.
|
|
138
|
-
if File.basename(file) == "collection.yml"
|
|
139
|
-
schemas_yaml_path = File.join(File.dirname(file), "schemas.yaml")
|
|
140
|
-
if schemas_yaml_path && File.exist?(schemas_yaml_path)
|
|
141
|
-
@schema_config = Expressir::SchemaManifest.from_file(schemas_yaml_path)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
42
|
end
|
|
146
43
|
end
|
data/lib/suma/eengine/wrapper.rb
CHANGED