suma 0.2.5 → 0.2.6
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/.github/workflows/rake.yml +3 -0
- data/.github/workflows/release.yml +5 -1
- data/.rubocop_todo.yml +78 -26
- data/CLAUDE.md +76 -0
- data/Gemfile +3 -1
- data/README.adoc +131 -0
- data/lib/suma/cli/build.rb +2 -3
- data/lib/suma/cli/check_svg_quality.rb +178 -0
- data/lib/suma/cli/compare.rb +7 -158
- data/lib/suma/cli/export.rb +1 -7
- data/lib/suma/cli/extract_terms.rb +7 -648
- data/lib/suma/cli/generate_schemas.rb +9 -123
- data/lib/suma/cli/validate_links.rb +15 -290
- data/lib/suma/cli.rb +39 -0
- data/lib/suma/collection_manifest.rb +3 -4
- data/lib/suma/express_schema.rb +43 -30
- data/lib/suma/jsdai/figure_xml.rb +12 -9
- data/lib/suma/jsdai.rb +0 -6
- data/lib/suma/link_validator.rb +203 -0
- data/lib/suma/processor.rb +75 -101
- data/lib/suma/schema_attachment.rb +2 -29
- data/lib/suma/schema_collection.rb +1 -32
- data/lib/suma/schema_comparer.rb +116 -0
- data/lib/suma/schema_document.rb +0 -14
- data/lib/suma/schema_exporter.rb +16 -28
- data/lib/suma/schema_index.rb +53 -0
- data/lib/suma/schema_manifest_generator.rb +105 -0
- data/lib/suma/svg_quality/batch_report.rb +80 -0
- data/lib/suma/svg_quality/formatters/json_formatter.rb +30 -0
- data/lib/suma/svg_quality/formatters/terminal_formatter.rb +168 -0
- data/lib/suma/svg_quality/formatters/yaml_formatter.rb +32 -0
- data/lib/suma/svg_quality/report.rb +52 -0
- data/lib/suma/svg_quality.rb +28 -0
- data/lib/suma/term_extractor.rb +393 -0
- data/lib/suma/utils.rb +10 -2
- data/lib/suma/version.rb +1 -1
- data/lib/suma.rb +3 -2
- data/suma.gemspec +3 -2
- metadata +33 -7
- data/lib/suma/export_standalone_schema.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e6db67576d42aaaed54f90f72960ad405774d4084be5f7c0e8f1b277ebbbd50
|
|
4
|
+
data.tar.gz: ed5cdf3967496436950306f0edcf553751bb3da06c4e4582a014b8e7610e6d35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0e4e8fe19fb5b9ec7c8023258e55a8ed100c98bb4ee271898562eeed13d5e1646d09c673e0053f2271827c8413408d5cb9295e94cc39d298be7161d33a0805c2
|
|
7
|
+
data.tar.gz: 8110684a885977266204dace36b4b5bb8a88113c1b410aa78c19d8ad4c58c2a49858f14f9495e433414d43b0bd13716a49e47eb8ef7724ee34306826a215ff15
|
data/.github/workflows/rake.yml
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
# See https://github.com/metanorma/cimas
|
|
3
3
|
name: release
|
|
4
4
|
|
|
5
|
+
permissions:
|
|
6
|
+
contents: write
|
|
7
|
+
packages: write
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
5
10
|
on:
|
|
6
11
|
workflow_dispatch:
|
|
7
12
|
inputs:
|
|
@@ -22,4 +27,3 @@ jobs:
|
|
|
22
27
|
secrets:
|
|
23
28
|
rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
|
|
24
29
|
pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
|
|
25
|
-
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on
|
|
3
|
+
# on 2026-05-11 08:57:26 UTC using RuboCop version 1.86.1.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
9
|
# Offense count: 3
|
|
10
|
-
# Configuration parameters: Severity.
|
|
11
10
|
Gemspec/DuplicatedAssignment:
|
|
12
11
|
Exclude:
|
|
13
12
|
- 'suma.gemspec'
|
|
14
13
|
|
|
15
14
|
# Offense count: 1
|
|
16
|
-
# Configuration parameters: Severity.
|
|
17
15
|
Gemspec/RequiredRubyVersion:
|
|
18
16
|
Exclude:
|
|
19
17
|
- 'suma.gemspec'
|
|
20
18
|
|
|
21
|
-
# Offense count:
|
|
19
|
+
# Offense count: 120
|
|
22
20
|
# This cop supports safe autocorrection (--autocorrect).
|
|
23
|
-
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes,
|
|
21
|
+
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
24
22
|
# URISchemes: http, https
|
|
25
23
|
Layout/LineLength:
|
|
26
24
|
Enabled: false
|
|
27
25
|
|
|
28
|
-
# Offense count:
|
|
26
|
+
# Offense count: 4
|
|
29
27
|
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
|
30
28
|
Lint/DuplicateBranch:
|
|
31
29
|
Exclude:
|
|
32
30
|
- 'lib/suma/jsdai/figure.rb'
|
|
31
|
+
- 'lib/suma/term_extractor.rb'
|
|
33
32
|
|
|
34
33
|
# Offense count: 1
|
|
35
34
|
Lint/DuplicateMethods:
|
|
@@ -41,48 +40,93 @@ Lint/IneffectiveAccessModifier:
|
|
|
41
40
|
Exclude:
|
|
42
41
|
- 'lib/suma/cli/export.rb'
|
|
43
42
|
|
|
44
|
-
# Offense count:
|
|
43
|
+
# Offense count: 1
|
|
44
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
45
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
46
|
+
# NotImplementedExceptions: NotImplementedError
|
|
47
|
+
Lint/UnusedMethodArgument:
|
|
48
|
+
Exclude:
|
|
49
|
+
- 'lib/suma/svg_quality.rb'
|
|
50
|
+
|
|
51
|
+
# Offense count: 29
|
|
45
52
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
46
53
|
Metrics/AbcSize:
|
|
47
54
|
Exclude:
|
|
48
|
-
- 'lib/suma/cli
|
|
55
|
+
- 'lib/suma/cli.rb'
|
|
56
|
+
- 'lib/suma/cli/check_svg_quality.rb'
|
|
49
57
|
- 'lib/suma/jsdai/figure.rb'
|
|
50
58
|
- 'lib/suma/jsdai/figure_image.rb'
|
|
59
|
+
- 'lib/suma/link_validator.rb'
|
|
60
|
+
- 'lib/suma/processor.rb'
|
|
51
61
|
- 'lib/suma/schema_attachment.rb'
|
|
62
|
+
- 'lib/suma/schema_comparer.rb'
|
|
52
63
|
- 'lib/suma/schema_document.rb'
|
|
64
|
+
- 'lib/suma/schema_manifest_generator.rb'
|
|
65
|
+
- 'lib/suma/svg_quality/formatters/terminal_formatter.rb'
|
|
66
|
+
- 'lib/suma/term_extractor.rb'
|
|
53
67
|
- 'lib/suma/thor_ext.rb'
|
|
54
68
|
|
|
55
|
-
# Offense count:
|
|
69
|
+
# Offense count: 1
|
|
70
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
|
71
|
+
# AllowedMethods: refine
|
|
72
|
+
Metrics/BlockLength:
|
|
73
|
+
Max: 54
|
|
74
|
+
|
|
75
|
+
# Offense count: 14
|
|
56
76
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
57
77
|
Metrics/CyclomaticComplexity:
|
|
58
78
|
Exclude:
|
|
59
|
-
- 'lib/suma/cli/
|
|
79
|
+
- 'lib/suma/cli/check_svg_quality.rb'
|
|
60
80
|
- 'lib/suma/eengine/wrapper.rb'
|
|
81
|
+
- 'lib/suma/express_schema.rb'
|
|
61
82
|
- 'lib/suma/jsdai/figure.rb'
|
|
62
83
|
- 'lib/suma/jsdai/figure_image.rb'
|
|
84
|
+
- 'lib/suma/link_validator.rb'
|
|
85
|
+
- 'lib/suma/schema_comparer.rb'
|
|
86
|
+
- 'lib/suma/svg_quality/formatters/terminal_formatter.rb'
|
|
87
|
+
- 'lib/suma/term_extractor.rb'
|
|
63
88
|
- 'lib/suma/thor_ext.rb'
|
|
64
89
|
|
|
65
|
-
# Offense count:
|
|
90
|
+
# Offense count: 25
|
|
66
91
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
67
92
|
Metrics/MethodLength:
|
|
68
|
-
Max:
|
|
93
|
+
Max: 59
|
|
69
94
|
|
|
70
|
-
# Offense count:
|
|
95
|
+
# Offense count: 2
|
|
96
|
+
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
|
97
|
+
Metrics/ParameterLists:
|
|
98
|
+
Max: 9
|
|
99
|
+
|
|
100
|
+
# Offense count: 8
|
|
71
101
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
72
102
|
Metrics/PerceivedComplexity:
|
|
73
103
|
Exclude:
|
|
74
|
-
- 'lib/suma/cli/compare.rb'
|
|
75
104
|
- 'lib/suma/eengine/wrapper.rb'
|
|
76
105
|
- 'lib/suma/jsdai/figure_image.rb'
|
|
106
|
+
- 'lib/suma/link_validator.rb'
|
|
107
|
+
- 'lib/suma/schema_comparer.rb'
|
|
108
|
+
- 'lib/suma/svg_quality/formatters/terminal_formatter.rb'
|
|
109
|
+
- 'lib/suma/term_extractor.rb'
|
|
77
110
|
|
|
78
111
|
# Offense count: 1
|
|
79
112
|
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
|
|
80
113
|
# AllowedMethods: call
|
|
81
|
-
# WaywardPredicates: nonzero?
|
|
114
|
+
# WaywardPredicates: infinite?, nonzero?
|
|
82
115
|
Naming/PredicateMethod:
|
|
83
116
|
Exclude:
|
|
84
117
|
- 'lib/suma/eengine/wrapper.rb'
|
|
85
118
|
|
|
119
|
+
# Offense count: 1
|
|
120
|
+
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
|
|
121
|
+
# NamePrefix: is_, has_, have_, does_
|
|
122
|
+
# ForbiddenPrefixes: is_, has_, have_, does_
|
|
123
|
+
# AllowedMethods: is_a?
|
|
124
|
+
# MethodDefinitionMacros: define_method, define_singleton_method
|
|
125
|
+
Naming/PredicatePrefix:
|
|
126
|
+
Exclude:
|
|
127
|
+
- 'spec/**/*'
|
|
128
|
+
- 'lib/suma/term_extractor.rb'
|
|
129
|
+
|
|
86
130
|
# Offense count: 2
|
|
87
131
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
|
88
132
|
# SupportedStyles: snake_case, normalcase, non_integer
|
|
@@ -95,9 +139,9 @@ Naming/VariableNumber:
|
|
|
95
139
|
# Configuration parameters: MinSize.
|
|
96
140
|
Performance/CollectionLiteralInLoop:
|
|
97
141
|
Exclude:
|
|
98
|
-
- 'spec/suma/cli/compare_spec.rb'
|
|
99
142
|
- 'spec/suma/cli/export_spec.rb'
|
|
100
143
|
- 'spec/suma/cli_spec.rb'
|
|
144
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
101
145
|
|
|
102
146
|
# Offense count: 2
|
|
103
147
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
|
@@ -106,40 +150,40 @@ RSpec/ContextWording:
|
|
|
106
150
|
Exclude:
|
|
107
151
|
- 'spec/suma/cli/export_spec.rb'
|
|
108
152
|
|
|
109
|
-
# Offense count:
|
|
153
|
+
# Offense count: 71
|
|
110
154
|
# Configuration parameters: CountAsOne.
|
|
111
155
|
RSpec/ExampleLength:
|
|
112
|
-
Max:
|
|
156
|
+
Max: 60
|
|
113
157
|
|
|
114
158
|
# Offense count: 4
|
|
115
159
|
# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
|
|
116
160
|
RSpec/IndexedLet:
|
|
117
161
|
Exclude:
|
|
118
|
-
- 'spec/suma/cli/compare_spec.rb'
|
|
119
162
|
- 'spec/suma/cli/export_spec.rb'
|
|
163
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
120
164
|
|
|
121
165
|
# Offense count: 22
|
|
122
166
|
# Configuration parameters: AssignmentOnly.
|
|
123
167
|
RSpec/InstanceVariable:
|
|
124
168
|
Exclude:
|
|
125
|
-
- 'spec/suma/
|
|
169
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
126
170
|
|
|
127
171
|
# Offense count: 1
|
|
128
172
|
# This cop supports safe autocorrection (--autocorrect).
|
|
129
173
|
RSpec/IteratedExpectation:
|
|
130
174
|
Exclude:
|
|
131
|
-
- 'spec/suma/
|
|
175
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
132
176
|
|
|
133
177
|
# Offense count: 1
|
|
134
178
|
# Configuration parameters: EnforcedStyle.
|
|
135
179
|
# SupportedStyles: have_received, receive
|
|
136
180
|
RSpec/MessageSpies:
|
|
137
181
|
Exclude:
|
|
138
|
-
- 'spec/suma/
|
|
182
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
139
183
|
|
|
140
|
-
# Offense count:
|
|
184
|
+
# Offense count: 50
|
|
141
185
|
RSpec/MultipleExpectations:
|
|
142
|
-
Max:
|
|
186
|
+
Max: 21
|
|
143
187
|
|
|
144
188
|
# Offense count: 3
|
|
145
189
|
# Configuration parameters: AllowedGroups.
|
|
@@ -147,7 +191,8 @@ RSpec/NestedGroups:
|
|
|
147
191
|
Max: 4
|
|
148
192
|
|
|
149
193
|
# Offense count: 1
|
|
150
|
-
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata.
|
|
194
|
+
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
195
|
+
# SupportedInflectors: default, active_support
|
|
151
196
|
RSpec/SpecFilePathFormat:
|
|
152
197
|
Exclude:
|
|
153
198
|
- '**/spec/routing/**/*'
|
|
@@ -156,4 +201,11 @@ RSpec/SpecFilePathFormat:
|
|
|
156
201
|
# Offense count: 1
|
|
157
202
|
RSpec/StubbedMock:
|
|
158
203
|
Exclude:
|
|
159
|
-
- 'spec/suma/
|
|
204
|
+
- 'spec/suma/schema_comparer_integration_spec.rb'
|
|
205
|
+
|
|
206
|
+
# Offense count: 1
|
|
207
|
+
# Configuration parameters: AllowedMethods.
|
|
208
|
+
# AllowedMethods: respond_to_missing?
|
|
209
|
+
Style/OptionalBooleanParameter:
|
|
210
|
+
Exclude:
|
|
211
|
+
- 'lib/suma/cli/check_svg_quality.rb'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Suma is a Ruby gem for processing EXPRESS schemas (ISO 10303 STEP standards). It reads Metanorma site manifests, discovers EXPRESS schemas, compiles them into documentation, and can export/compare schemas.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
### Development
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bundle install # Install dependencies
|
|
15
|
+
rake spec # Run RSpec tests
|
|
16
|
+
rake rubocop # Run RuboCop linter
|
|
17
|
+
rake # Run both tests and linter (default task)
|
|
18
|
+
rspec spec/path/to_spec.rb # Run a single spec file
|
|
19
|
+
rspec spec/path/to_spec.rb:42 # Run a single test at line 42
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### CLI usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
exe/suma build METANORMA_YAML # Build collection from site manifest
|
|
26
|
+
exe/suma export -o OUTPUT_DIR schema1.yml schema2.exp # Export schemas (YAML manifest or .exp files)
|
|
27
|
+
exe/suma compare TRIAL_SCHEMA REFERENCE_SCHEMA -v VER # Compare schemas, generate .changes.yaml
|
|
28
|
+
exe/suma reformat PATH # Reformat EXP files (use -r for recursive)
|
|
29
|
+
exe/suma generate-schemas METANORMA_YAML SCHEMAS_YAML # Generate schema manifest from site manifest
|
|
30
|
+
exe/suma extract-terms SCHEMA_YAML GLOSSARIST_DIR # Extract terms to Glossarist v2 format
|
|
31
|
+
exe/suma validate links SCHEMAS_FILE DOCS_PATH # Validate EXPRESS cross-reference links
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
### Core pipeline (build command)
|
|
37
|
+
|
|
38
|
+
1. `Processor.run` receives a Metanorma site manifest YAML
|
|
39
|
+
2. `SiteConfig` reads the site manifest, finds collection YAML paths
|
|
40
|
+
3. `CollectionConfig` extends `Metanorma::Collection::Config` with a `CollectionManifest` that discovers `schemas.yaml` files
|
|
41
|
+
4. `SchemaCollection` loads all discovered schemas via `Expressir`, exports plain `.exp` files, and compiles documentation (Metanorma `.adoc` → XML/HTML)
|
|
42
|
+
5. `SchemaExporter` handles exporting schemas to a directory (with optional ZIP packaging)
|
|
43
|
+
|
|
44
|
+
### Key classes
|
|
45
|
+
|
|
46
|
+
- **`ExpressSchema`** — wraps a single EXPRESS schema file; parses via `Expressir::Express::Parser`, can output plain or annotated `.exp`
|
|
47
|
+
- **`SchemaAttachment`** — compiles one schema into a Metanorma `.adoc` document and renders it via `Metanorma::Compile`
|
|
48
|
+
- **`SchemaDocument`** (extends `SchemaAttachment`) — adds cross-reference bookmarks and uses XML-only output
|
|
49
|
+
- **`SchemaCollection`** — orchestrates processing of all schemas from a config
|
|
50
|
+
- **`SchemaExporter`** — standalone export of schemas from manifest or `.exp` files to a directory, with optional ZIP
|
|
51
|
+
- **`CollectionManifest`** — traverses collection YAML files, builds unified `Expressir::SchemaManifest`
|
|
52
|
+
|
|
53
|
+
### CLI structure
|
|
54
|
+
|
|
55
|
+
- `Suma::Cli::Core` (Thor subclass) — top-level CLI entrypoint
|
|
56
|
+
- Subcommands delegate to `Cli::Build`, `Cli::Export`, `Cli::Compare`, `Cli::Validate`, `Cli::Reformat`, `Cli::GenerateSchemas`, `Cli::ExtractTerms`, `Cli::ConvertJsdai`
|
|
57
|
+
- Thor extension (`ThorExt::Start`) adds `-h`/`--help` support and error formatting
|
|
58
|
+
|
|
59
|
+
### External dependencies
|
|
60
|
+
|
|
61
|
+
- **expressir** — EXPRESS schema parsing and manifest handling
|
|
62
|
+
- **metanorma** — document compilation (`.adoc` → XML/HTML/PDF)
|
|
63
|
+
- **lutaml-model** — YAML/XML model serialization (used in `SiteConfig`, `CollectionConfig`)
|
|
64
|
+
- **glossarist** — term extraction output format
|
|
65
|
+
- **eengine** (optional, external binary) — schema comparison via `Eengine::Wrapper`
|
|
66
|
+
|
|
67
|
+
### Schema comparison flow
|
|
68
|
+
|
|
69
|
+
The `compare` command uses an external `eengine` binary to diff two EXPRESS schemas, producing XML. `EengineConverter` then converts that XML into a `.changes.yaml` file managed by `Expressir::Changes::SchemaChange`.
|
|
70
|
+
|
|
71
|
+
## Code style
|
|
72
|
+
|
|
73
|
+
- Ruby 3.0+ with `frozen_string_literal: true` in every file
|
|
74
|
+
- RuboCop with performance, rake, and rspec plugins; inherits from riboseinc OSS guide
|
|
75
|
+
- Some CLI files are excluded from RuboCop in `.rubocop.yml`
|
|
76
|
+
- Use `Utils.log` for user-facing output (prefixes with `[suma]`)
|
data/Gemfile
CHANGED
|
@@ -6,7 +6,9 @@ source "https://rubygems.org"
|
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
8
|
gem "canon"
|
|
9
|
-
gem "metanorma
|
|
9
|
+
gem "metanorma", github: "metanorma/metanorma", branch: "main"
|
|
10
|
+
gem "metanorma-plugin-lutaml", github: "metanorma/metanorma-plugin-lutaml", branch: "main"
|
|
11
|
+
gem "metanorma-standoc", github: "metanorma/metanorma-standoc", branch: "main"
|
|
10
12
|
gem "nokogiri"
|
|
11
13
|
gem "openssl", "~> 3.0"
|
|
12
14
|
gem "rake"
|
data/README.adoc
CHANGED
|
@@ -864,6 +864,137 @@ For complete documentation of all Expressir commands and options, see the
|
|
|
864
864
|
https://github.com/lutaml/expressir[Expressir documentation].
|
|
865
865
|
|
|
866
866
|
|
|
867
|
+
=== Check SVG quality command
|
|
868
|
+
|
|
869
|
+
==== General
|
|
870
|
+
|
|
871
|
+
The `check_svg_quality` command validates SVG files for conformance and quality
|
|
872
|
+
using the https://github.com/claricle/svg_conform[svg_conform] library. It supports
|
|
873
|
+
both batch directory scanning and single file analysis.
|
|
874
|
+
|
|
875
|
+
By default, it uses the `metanorma` profile which allows raster images
|
|
876
|
+
(PNG, JPEG) and other features specific to Metanorma documentation.
|
|
877
|
+
|
|
878
|
+
[source,sh]
|
|
879
|
+
----
|
|
880
|
+
$ suma check_svg_quality [PATH] [options]
|
|
881
|
+
----
|
|
882
|
+
|
|
883
|
+
Parameters:
|
|
884
|
+
|
|
885
|
+
`PATH`:: Path to directory containing SVG files or a single SVG file.
|
|
886
|
+
Defaults to `schemas` directory.
|
|
887
|
+
|
|
888
|
+
==== Batch directory scanning
|
|
889
|
+
|
|
890
|
+
When given a directory, it scans all SVG files and produces a quality report
|
|
891
|
+
sorted by error count (most problematic files first).
|
|
892
|
+
|
|
893
|
+
[example]
|
|
894
|
+
.Check all SVG files in schemas directory
|
|
895
|
+
[source,sh]
|
|
896
|
+
----
|
|
897
|
+
$ suma check_svg_quality ../iso-10303/schemas
|
|
898
|
+
----
|
|
899
|
+
|
|
900
|
+
.Output
|
|
901
|
+
----
|
|
902
|
+
🔍 Scanning 3878 SVG files...
|
|
903
|
+
|
|
904
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
905
|
+
║ 🔍 SVG Quality Report Sorted by error count (most first) ║
|
|
906
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
907
|
+
|
|
908
|
+
📊 OVERVIEW
|
|
909
|
+
|
|
910
|
+
● Total Files : 3878
|
|
911
|
+
● Valid : 3845 ✅
|
|
912
|
+
● Invalid : 33 ❌
|
|
913
|
+
...
|
|
914
|
+
|
|
915
|
+
💥 CRITICAL QUALITY (2 files)
|
|
916
|
+
|
|
917
|
+
✗ 0/100 813 errors schemas/resources/action_schema/action_schemaexpg2.svg
|
|
918
|
+
✗ 25/100 156 errors schemas/modules/activity/mimexpg1.svg
|
|
919
|
+
----
|
|
920
|
+
|
|
921
|
+
==== Single file analysis
|
|
922
|
+
|
|
923
|
+
When given a path to a single SVG file, it shows detailed error breakdown
|
|
924
|
+
grouped by requirement type.
|
|
925
|
+
|
|
926
|
+
[example]
|
|
927
|
+
.Analyze a single SVG file
|
|
928
|
+
[source,sh]
|
|
929
|
+
----
|
|
930
|
+
$ suma check_svg_quality schemas/resources/action_schema/action_schemaexpg2.svg
|
|
931
|
+
----
|
|
932
|
+
|
|
933
|
+
.Output
|
|
934
|
+
----
|
|
935
|
+
📄 SVG Quality Report: schemas/resources/action_schema/action_schemaexpg2.svg
|
|
936
|
+
|
|
937
|
+
Valid: NO ❌
|
|
938
|
+
Errors: 813
|
|
939
|
+
|
|
940
|
+
📋 Error Details
|
|
941
|
+
|
|
942
|
+
style (56 occurrences)
|
|
943
|
+
- Style property 'enable-background' removed
|
|
944
|
+
- Style property 'overflow' removed
|
|
945
|
+
...
|
|
946
|
+
|
|
947
|
+
allowed_elements (109 occurrences)
|
|
948
|
+
- The element 'image' is not allowed as a child of 'svg'
|
|
949
|
+
...
|
|
950
|
+
|
|
951
|
+
color_restrictions (108 occurrences)
|
|
952
|
+
- Color 'rgb(33, 128, 255)' in style property 'fill' is not allowed
|
|
953
|
+
...
|
|
954
|
+
----
|
|
955
|
+
|
|
956
|
+
Options:
|
|
957
|
+
|
|
958
|
+
`--profile=PROFILE`:: Validation profile to use (default: `metanorma`).
|
|
959
|
+
Other options: `svg_1_2_rfc`, `svg_1_2_rfc_with_rdf`, `base`, `lucid_fix`
|
|
960
|
+
|
|
961
|
+
`--sort=MODE`:: Sort order: `errors` (most errors first, default) or
|
|
962
|
+
`quality` (lowest quality scores first)
|
|
963
|
+
|
|
964
|
+
`--format=FORMAT`:: Output format: `terminal` (default), `json`, or `yaml`
|
|
965
|
+
|
|
966
|
+
`--output=PATH`, `-o PATH`:: Write output to file instead of stdout
|
|
967
|
+
|
|
968
|
+
`--min_errors=N`:: Filter to show only files with at least N errors
|
|
969
|
+
|
|
970
|
+
`--limit=N`:: Limit output to top N files
|
|
971
|
+
|
|
972
|
+
`--progress`:: Show progress during batch scanning
|
|
973
|
+
|
|
974
|
+
`--summary-only`:: Show only summary, not individual file details
|
|
975
|
+
|
|
976
|
+
[example]
|
|
977
|
+
.Check with custom profile
|
|
978
|
+
[source,sh]
|
|
979
|
+
----
|
|
980
|
+
$ suma check_svg_quality schemas/ --profile svg_1_2_rfc
|
|
981
|
+
----
|
|
982
|
+
|
|
983
|
+
[example]
|
|
984
|
+
.Output as JSON
|
|
985
|
+
[source,sh]
|
|
986
|
+
----
|
|
987
|
+
$ suma check_svg_quality schemas/ --format json --output quality_report.json
|
|
988
|
+
----
|
|
989
|
+
|
|
990
|
+
[example]
|
|
991
|
+
.Show only files with 100+ errors
|
|
992
|
+
[source,sh]
|
|
993
|
+
----
|
|
994
|
+
$ suma check_svg_quality schemas/ --min_errors 100
|
|
995
|
+
----
|
|
996
|
+
|
|
997
|
+
|
|
867
998
|
== Usage: Ruby
|
|
868
999
|
|
|
869
1000
|
=== General
|
data/lib/suma/cli/build.rb
CHANGED
|
@@ -31,16 +31,15 @@ module Suma
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
33
|
def run(manifest, options)
|
|
34
|
-
# Set schemas_all_path to match metanorma_yaml_path
|
|
35
34
|
schemas_all_path = options[:schemas_all_path] ||
|
|
36
35
|
manifest.gsub("metanorma", "schemas")
|
|
37
36
|
|
|
38
|
-
Processor.
|
|
37
|
+
Processor.new(
|
|
39
38
|
metanorma_yaml_path: manifest,
|
|
40
39
|
schemas_all_path: schemas_all_path,
|
|
41
40
|
compile: options[:compile],
|
|
42
41
|
output_directory: "_site",
|
|
43
|
-
)
|
|
42
|
+
).run
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
def log_error(error)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require_relative "../svg_quality"
|
|
5
|
+
require_relative "../svg_quality/report"
|
|
6
|
+
require_relative "../svg_quality/batch_report"
|
|
7
|
+
require_relative "../svg_quality/formatters/terminal_formatter"
|
|
8
|
+
require_relative "../svg_quality/formatters/json_formatter"
|
|
9
|
+
require_relative "../svg_quality/formatters/yaml_formatter"
|
|
10
|
+
|
|
11
|
+
module Suma
|
|
12
|
+
module Cli
|
|
13
|
+
# Check SVG quality using svg_conform Validator API - thin CLI wrapper
|
|
14
|
+
class CheckSvgQuality
|
|
15
|
+
DATA_PATH = "schemas"
|
|
16
|
+
DEFAULT_PATTERN = "**/*.svg"
|
|
17
|
+
DEFAULT_PROFILE = :metanorma
|
|
18
|
+
|
|
19
|
+
def initialize(pattern: DEFAULT_PATTERN, profile: DEFAULT_PROFILE,
|
|
20
|
+
format: "terminal", output: nil, min_errors: nil,
|
|
21
|
+
summary_only: false, progress: false, limit: nil,
|
|
22
|
+
sort: "errors")
|
|
23
|
+
@options = {
|
|
24
|
+
pattern: pattern,
|
|
25
|
+
profile: profile,
|
|
26
|
+
format: format,
|
|
27
|
+
output: output,
|
|
28
|
+
min_errors: min_errors,
|
|
29
|
+
summary_only: summary_only,
|
|
30
|
+
progress: progress,
|
|
31
|
+
limit: limit,
|
|
32
|
+
sort: sort.to_sym,
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run(path = DATA_PATH)
|
|
37
|
+
require "svg_conform"
|
|
38
|
+
|
|
39
|
+
path_obj = Pathname.new(path).expand_path
|
|
40
|
+
|
|
41
|
+
# Enable progress by default when outputting to terminal
|
|
42
|
+
show_progress = options[:progress] || ($stdout.tty? && !options[:output])
|
|
43
|
+
if show_progress
|
|
44
|
+
$stdout.sync = true
|
|
45
|
+
$stderr.sync = true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if path_obj.file?
|
|
49
|
+
# Single file mode - show detailed errors
|
|
50
|
+
analyze_single_file(path_obj)
|
|
51
|
+
else
|
|
52
|
+
# Directory mode - show batch report
|
|
53
|
+
svg_files = find_svg_files(path_obj)
|
|
54
|
+
|
|
55
|
+
if svg_files.empty?
|
|
56
|
+
puts "No SVG files found in #{path}"
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
puts "🔍 Scanning #{svg_files.size} SVG files..."
|
|
61
|
+
puts
|
|
62
|
+
|
|
63
|
+
reports = analyze_files_one_by_one(svg_files, show_progress)
|
|
64
|
+
batch_report = SvgQuality::BatchReport.new(reports)
|
|
65
|
+
sorted_report = sort_report(batch_report)
|
|
66
|
+
output_report(sorted_report)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def analyze_single_file(path)
|
|
71
|
+
validator = SvgConform::Validator.new
|
|
72
|
+
result = validator.validate_file(path.to_s, profile: options[:profile])
|
|
73
|
+
|
|
74
|
+
puts "📄 SVG Quality Report: #{path}"
|
|
75
|
+
puts ""
|
|
76
|
+
puts " Valid: #{result.valid? ? 'YES ✅' : 'NO ❌'}"
|
|
77
|
+
puts " Errors: #{result.error_count}"
|
|
78
|
+
puts ""
|
|
79
|
+
|
|
80
|
+
if result.errors.any?
|
|
81
|
+
puts " 📋 Error Details"
|
|
82
|
+
puts ""
|
|
83
|
+
|
|
84
|
+
# Group errors by requirement_id
|
|
85
|
+
by_req = result.errors.group_by(&:requirement_id)
|
|
86
|
+
|
|
87
|
+
by_req.each do |req_id, errors|
|
|
88
|
+
puts " #{req_id} (#{errors.size} occurrences)"
|
|
89
|
+
errors.first(5).each do |e|
|
|
90
|
+
puts " - #{e.message}"
|
|
91
|
+
end
|
|
92
|
+
if errors.size > 5
|
|
93
|
+
puts " ... and #{errors.size - 5} more"
|
|
94
|
+
end
|
|
95
|
+
puts ""
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
attr_reader :options
|
|
103
|
+
|
|
104
|
+
def find_svg_files(path)
|
|
105
|
+
if path.directory?
|
|
106
|
+
Pathname.glob(path.join(options[:pattern])).select(&:file?)
|
|
107
|
+
elsif path.file? && path.extname == ".svg"
|
|
108
|
+
[path]
|
|
109
|
+
else
|
|
110
|
+
[]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def analyze_files_one_by_one(files, show_progress = false)
|
|
115
|
+
validator = SvgConform::Validator.new
|
|
116
|
+
reports = []
|
|
117
|
+
|
|
118
|
+
files.each_with_index do |file, index|
|
|
119
|
+
result = validator.validate_file(file.to_s,
|
|
120
|
+
profile: options[:profile])
|
|
121
|
+
report = SvgQuality::Report.new(file.to_s, result)
|
|
122
|
+
reports << report
|
|
123
|
+
|
|
124
|
+
if show_progress
|
|
125
|
+
tier = report.quality_tier
|
|
126
|
+
status = report.valid? ? "✅" : "❌"
|
|
127
|
+
msg = " [#{index + 1}/#{files.size}] #{tier[:emoji]} #{report.error_count} errors #{status} #{shorten_path(file)}\n"
|
|
128
|
+
$stderr.print msg
|
|
129
|
+
$stderr.flush
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
reports
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def sort_report(batch_report)
|
|
137
|
+
case options[:sort]
|
|
138
|
+
when :quality
|
|
139
|
+
batch_report.sort_by_quality
|
|
140
|
+
else
|
|
141
|
+
batch_report.sort_by_errors
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def output_report(batch_report)
|
|
146
|
+
filtered = batch_report.filter_by_min_errors(options[:min_errors])
|
|
147
|
+
limited = filtered.limit(options[:limit])
|
|
148
|
+
|
|
149
|
+
formatter = case options[:format].to_sym
|
|
150
|
+
when :json
|
|
151
|
+
SvgQuality::Formatters::JsonFormatter.new(limited,
|
|
152
|
+
output: options[:output])
|
|
153
|
+
when :yaml
|
|
154
|
+
SvgQuality::Formatters::YamlFormatter.new(limited,
|
|
155
|
+
output: options[:output])
|
|
156
|
+
else
|
|
157
|
+
SvgQuality::Formatters::TerminalFormatter.new(limited,
|
|
158
|
+
output: options[:output], sort: options[:sort])
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
puts formatter.format
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def shorten_path(path)
|
|
165
|
+
p = Pathname.new(path)
|
|
166
|
+
if p.absolute?
|
|
167
|
+
begin
|
|
168
|
+
p.relative_path_from(Pathname.pwd)
|
|
169
|
+
rescue StandardError
|
|
170
|
+
p
|
|
171
|
+
end
|
|
172
|
+
else
|
|
173
|
+
p
|
|
174
|
+
end.to_s
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|