suma 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +10 -1
- data/.rubocop_todo.yml +170 -13
- data/CLAUDE.md +37 -11
- data/Gemfile +3 -3
- data/README.adoc +57 -1
- data/exe/suma +1 -1
- data/lib/suma/cli/build.rb +0 -5
- data/lib/suma/cli/check_svg_quality.rb +0 -6
- 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 +0 -3
- 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 +0 -1
- data/lib/suma/cli/validate.rb +0 -2
- data/lib/suma/cli/validate_links.rb +0 -2
- 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_schema.rb +0 -1
- data/lib/suma/jsdai/figure.rb +0 -3
- data/lib/suma/jsdai.rb +5 -2
- data/lib/suma/link_validator.rb +15 -7
- 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 +29 -33
- 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 +2 -18
- 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.rb +3 -1
- data/lib/suma/term_extractor.rb +119 -46
- data/lib/suma/urn.rb +61 -0
- data/lib/suma/version.rb +1 -1
- data/lib/suma.rb +31 -3
- data/suma.gemspec +1 -1
- metadata +24 -6
- data/lib/suma/schema_attachment.rb +0 -103
- data/lib/suma/schema_document.rb +0 -118
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "expressir"
|
|
6
|
+
require "glossarist"
|
|
7
|
+
|
|
8
|
+
module Suma
|
|
9
|
+
# Generates a Glossarist v3 register.yaml from an EXPRESS schema manifest.
|
|
10
|
+
#
|
|
11
|
+
# The schema manifest (schemas-smrl-part-2.yml) is the single source of
|
|
12
|
+
# truth for schema identities and paths. This class reads it, classifies
|
|
13
|
+
# each schema by type (resource/module), and emits a register.yaml with
|
|
14
|
+
# hierarchical sections and human-readable names.
|
|
15
|
+
#
|
|
16
|
+
# Architecture:
|
|
17
|
+
# - Classification: delegates to ExpressSchema::Type (DRY)
|
|
18
|
+
# - Naming: delegates to SchemaNaming (OCP — extend naming without
|
|
19
|
+
# touching this class)
|
|
20
|
+
# - Sections: built from Glossarist::Section models (model-driven)
|
|
21
|
+
# - URN semantics: delegates to Suma::Urn (OCP)
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# generator = RegisterManifestGenerator.new(
|
|
25
|
+
# "schemas-smrl-part-2.yml",
|
|
26
|
+
# urn: "urn:iso:std:iso:10303:-2:ed-2:en:tech:*",
|
|
27
|
+
# id: "iso10303-2-express",
|
|
28
|
+
# ref: "ISO 10303-2 EXPRESS Concepts",
|
|
29
|
+
# )
|
|
30
|
+
# generator.generate # writes register.yaml
|
|
31
|
+
class RegisterManifestGenerator
|
|
32
|
+
DEFAULT_OWNER = "ISO/TC 184/SC 4"
|
|
33
|
+
DEFAULT_STATUS = "current"
|
|
34
|
+
DEFAULT_ORDERING = "systematic"
|
|
35
|
+
DEFAULT_SCHEMA_TYPE = "glossarist"
|
|
36
|
+
DEFAULT_SCHEMA_VERSION = "3"
|
|
37
|
+
|
|
38
|
+
# @param schema_manifest_file [String] path to schemas-smrl-part-2.yml
|
|
39
|
+
# @param output_path [String] directory to write register.yaml
|
|
40
|
+
# @param urn [String] base URN for the dataset
|
|
41
|
+
# @param id [String] dataset identifier
|
|
42
|
+
# @param ref [String] human-readable reference label
|
|
43
|
+
# @param language_code [String] language for section names
|
|
44
|
+
# @param owner [String] dataset owner organisation
|
|
45
|
+
def initialize(schema_manifest_file, output_path, urn:, id:, ref:,
|
|
46
|
+
language_code: "eng", owner: DEFAULT_OWNER)
|
|
47
|
+
@schema_manifest_file = File.expand_path(schema_manifest_file)
|
|
48
|
+
@output_path = output_path
|
|
49
|
+
@urn = Suma::Urn.new(urn)
|
|
50
|
+
@id = id
|
|
51
|
+
@ref = ref
|
|
52
|
+
@language_code = language_code
|
|
53
|
+
@owner = owner
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Generate and write register.yaml.
|
|
57
|
+
#
|
|
58
|
+
# @return [Glossarist::DatasetRegister] the generated register
|
|
59
|
+
def generate
|
|
60
|
+
validate_inputs
|
|
61
|
+
schemas = load_schemas
|
|
62
|
+
register = build_register(schemas)
|
|
63
|
+
|
|
64
|
+
FileUtils.mkdir_p(@output_path)
|
|
65
|
+
output_file = File.join(@output_path, "register.yaml")
|
|
66
|
+
File.write(output_file, register.to_yaml)
|
|
67
|
+
Utils.log "Generated register.yaml: #{output_file}"
|
|
68
|
+
Utils.log " #{schemas.length} schemas in #{register.sections.length} categories"
|
|
69
|
+
|
|
70
|
+
register
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# Verify that the manifest path exists, is a regular file, and contains
|
|
76
|
+
# at least one schema entry. Called from {#generate} so both the CLI
|
|
77
|
+
# shell and direct construction get the same validation behavior.
|
|
78
|
+
#
|
|
79
|
+
# @raise [Errno::ENOENT] when the manifest path is missing or not a file
|
|
80
|
+
# @raise [ArgumentError] when the manifest is empty
|
|
81
|
+
def validate_inputs
|
|
82
|
+
unless File.exist?(@schema_manifest_file)
|
|
83
|
+
raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
|
|
84
|
+
"`#{@schema_manifest_file}` not found."
|
|
85
|
+
end
|
|
86
|
+
unless File.file?(@schema_manifest_file)
|
|
87
|
+
raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
|
|
88
|
+
"`#{@schema_manifest_file}` is not a file."
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Load all schemas from the manifest file as SchemaManifestEntry models.
|
|
93
|
+
#
|
|
94
|
+
# @return [Array<Expressir::SchemaManifestEntry>]
|
|
95
|
+
def load_schemas
|
|
96
|
+
manifest = Expressir::SchemaManifest.from_file(@schema_manifest_file)
|
|
97
|
+
schemas = manifest.schemas.sort_by { |s| s.id.downcase }
|
|
98
|
+
if schemas.empty?
|
|
99
|
+
raise ArgumentError, "No schemas found in manifest " \
|
|
100
|
+
"`#{@schema_manifest_file}`."
|
|
101
|
+
end
|
|
102
|
+
schemas
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Build a fully-populated DatasetRegister model.
|
|
106
|
+
#
|
|
107
|
+
# @param schemas [Array<Expressir::SchemaManifestEntry>]
|
|
108
|
+
# @return [Glossarist::DatasetRegister]
|
|
109
|
+
def build_register(schemas)
|
|
110
|
+
Glossarist::DatasetRegister.new(
|
|
111
|
+
schema_type: DEFAULT_SCHEMA_TYPE,
|
|
112
|
+
schema_version: DEFAULT_SCHEMA_VERSION,
|
|
113
|
+
id: @id,
|
|
114
|
+
ref: @ref,
|
|
115
|
+
urn: @urn.to_s,
|
|
116
|
+
urn_aliases: @urn.aliases,
|
|
117
|
+
status: DEFAULT_STATUS,
|
|
118
|
+
owner: @owner,
|
|
119
|
+
languages: [@language_code],
|
|
120
|
+
ordering: DEFAULT_ORDERING,
|
|
121
|
+
sections: build_sections(schemas),
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Build a list of top-level sections, one per category, in
|
|
126
|
+
# SchemaCategory::ALL declaration order. Categories with no
|
|
127
|
+
# schemas are omitted.
|
|
128
|
+
#
|
|
129
|
+
# @param schemas [Array<Expressir::SchemaManifestEntry>]
|
|
130
|
+
# @return [Array<Glossarist::Section>]
|
|
131
|
+
def build_sections(schemas)
|
|
132
|
+
groups = schemas.group_by do |s|
|
|
133
|
+
SchemaCategory.for_schema(id: s.id, path: s.path)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
SchemaCategory::ALL.filter_map do |category|
|
|
137
|
+
children = groups[category] || []
|
|
138
|
+
next if children.empty?
|
|
139
|
+
|
|
140
|
+
Glossarist::Section.new(
|
|
141
|
+
id: category.id,
|
|
142
|
+
names: { @language_code => category.label },
|
|
143
|
+
children: children.map { |s| build_section(s) },
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Build a single leaf section from a schema descriptor.
|
|
149
|
+
#
|
|
150
|
+
# @param schema [Expressir::SchemaManifestEntry]
|
|
151
|
+
# @return [Glossarist::Section]
|
|
152
|
+
def build_section(schema)
|
|
153
|
+
Glossarist::Section.new(
|
|
154
|
+
id: schema.id,
|
|
155
|
+
names: {
|
|
156
|
+
@language_code => SchemaNaming.prefixed_name(
|
|
157
|
+
schema.id, path: schema.path
|
|
158
|
+
),
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Suma
|
|
4
|
+
# Value object mapping an ExpressSchema::Type to its register/export category.
|
|
5
|
+
#
|
|
6
|
+
# Single source of truth for category identity across the codebase:
|
|
7
|
+
# directory layout (SchemaExporter), display prefix (SchemaNaming), and
|
|
8
|
+
# register.yaml sections (RegisterManifestGenerator) all derive from this.
|
|
9
|
+
#
|
|
10
|
+
# Each category carries: +id+ (slug used in register.yaml section id and
|
|
11
|
+
# export subdirectory), +label+ (human-readable section heading),
|
|
12
|
+
# +prefix+ (prefix used when forming display names, e.g. "Resource: ..."),
|
|
13
|
+
# +types+ (Array of ExpressSchema::Type symbols that belong to it),
|
|
14
|
+
# and +directory+ (subdirectory name; "." for the root output path).
|
|
15
|
+
class SchemaCategory
|
|
16
|
+
attr_reader :id, :label, :prefix, :types, :directory
|
|
17
|
+
|
|
18
|
+
def initialize(id:, label:, prefix:, types:, directory:)
|
|
19
|
+
@id = id
|
|
20
|
+
@label = label
|
|
21
|
+
@prefix = prefix
|
|
22
|
+
@types = types
|
|
23
|
+
@directory = directory
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def member?(type)
|
|
27
|
+
types.include?(type)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
RESOURCES = new(
|
|
31
|
+
id: "resources",
|
|
32
|
+
label: "Resources",
|
|
33
|
+
prefix: "Resource",
|
|
34
|
+
types: [ExpressSchema::Type::RESOURCE].freeze,
|
|
35
|
+
directory: "resources",
|
|
36
|
+
)
|
|
37
|
+
MODULES = new(
|
|
38
|
+
id: "modules",
|
|
39
|
+
label: "Application Modules",
|
|
40
|
+
prefix: "Module",
|
|
41
|
+
types: [ExpressSchema::Type::MODULE_ARM, ExpressSchema::Type::MODULE_MIM].freeze,
|
|
42
|
+
directory: "modules",
|
|
43
|
+
)
|
|
44
|
+
BUSINESS_OBJECT_MODELS = new(
|
|
45
|
+
id: "business_object_models",
|
|
46
|
+
label: "Business Object Models",
|
|
47
|
+
prefix: "Business Object Model",
|
|
48
|
+
types: [ExpressSchema::Type::BUSINESS_OBJECT_MODEL].freeze,
|
|
49
|
+
directory: "business_object_models",
|
|
50
|
+
)
|
|
51
|
+
CORE_MODEL = new(
|
|
52
|
+
id: "core_model",
|
|
53
|
+
label: "Core Model",
|
|
54
|
+
prefix: "Core Model",
|
|
55
|
+
types: [ExpressSchema::Type::CORE_MODEL].freeze,
|
|
56
|
+
directory: "core_model",
|
|
57
|
+
)
|
|
58
|
+
OTHER = new(
|
|
59
|
+
id: "other",
|
|
60
|
+
label: "Other Schemas",
|
|
61
|
+
prefix: "Schema",
|
|
62
|
+
types: [ExpressSchema::Type::STANDALONE].freeze,
|
|
63
|
+
directory: ".",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
ALL = [
|
|
67
|
+
RESOURCES,
|
|
68
|
+
MODULES,
|
|
69
|
+
BUSINESS_OBJECT_MODELS,
|
|
70
|
+
CORE_MODEL,
|
|
71
|
+
OTHER,
|
|
72
|
+
].freeze
|
|
73
|
+
|
|
74
|
+
def self.for_type(type)
|
|
75
|
+
ALL.find { |category| category.member?(type) } || OTHER
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.for_schema(id:, path:)
|
|
79
|
+
type = ExpressSchema::Type.classify(id: id, path: path)
|
|
80
|
+
for_type(type)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "express_schema"
|
|
4
|
-
require_relative "schema_attachment"
|
|
5
|
-
require_relative "schema_document"
|
|
6
|
-
require_relative "schema_exporter"
|
|
7
3
|
require "expressir"
|
|
8
|
-
require_relative "utils"
|
|
9
4
|
|
|
10
5
|
module Suma
|
|
11
6
|
class SchemaCollection
|
|
@@ -30,36 +25,14 @@ module Suma
|
|
|
30
25
|
@schema_name_to_docs[schema_name]
|
|
31
26
|
end
|
|
32
27
|
|
|
33
|
-
def process_schemas(schemas, klass)
|
|
34
|
-
schemas.each do |config_schema|
|
|
35
|
-
process_schema(config_schema, klass)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def process_schema(config_schema, klass)
|
|
40
|
-
s = ExpressSchema.new(
|
|
41
|
-
id: config_schema.id, path: config_schema.path.to_s,
|
|
42
|
-
output_path: @output_path_schemas.to_s
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
doc = klass.new(
|
|
46
|
-
schema: s, output_path: @output_path_docs.join(s.id),
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
@docs[s.id] = doc
|
|
50
|
-
@schemas[s.id] = s
|
|
51
|
-
@schema_name_to_docs[s.id] = doc
|
|
52
|
-
end
|
|
53
|
-
|
|
54
28
|
def finalize
|
|
55
|
-
process_schemas(@config.schemas,
|
|
56
|
-
|
|
57
|
-
manifest_entry = @manifest.lookup_schemas_only
|
|
29
|
+
process_schemas(@config.schemas, SchemaTemplate::Plain)
|
|
58
30
|
|
|
59
|
-
|
|
31
|
+
schemas_only_entries = ManifestTraverser.new(@manifest).find_schemas_only
|
|
32
|
+
schemas_only_entries.each do |entry|
|
|
60
33
|
next unless entry.schema_config
|
|
61
34
|
|
|
62
|
-
process_schemas(entry.schema_config.schemas,
|
|
35
|
+
process_schemas(entry.schema_config.schemas, SchemaTemplate::Document)
|
|
63
36
|
end
|
|
64
37
|
end
|
|
65
38
|
|
|
@@ -73,9 +46,32 @@ module Suma
|
|
|
73
46
|
)
|
|
74
47
|
exporter.export
|
|
75
48
|
|
|
76
|
-
docs.each_pair do |_schema_id,
|
|
77
|
-
|
|
49
|
+
docs.each_pair do |_schema_id, compiler|
|
|
50
|
+
compiler.compile
|
|
78
51
|
end
|
|
79
52
|
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def process_schemas(schemas, template_class)
|
|
57
|
+
schemas.each { |s| process_schema(s, template_class) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def process_schema(config_schema, template_class)
|
|
61
|
+
express = ExpressSchema.new(
|
|
62
|
+
id: config_schema.id, path: config_schema.path.to_s,
|
|
63
|
+
output_path: @output_path_schemas.to_s
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
compiler = SchemaCompiler.new(
|
|
67
|
+
schema: express,
|
|
68
|
+
output_path: @output_path_docs.join(express.id),
|
|
69
|
+
template: template_class.new(express.id),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@docs[express.id] = compiler
|
|
73
|
+
@schemas[express.id] = express
|
|
74
|
+
@schema_name_to_docs[express.id] = compiler
|
|
75
|
+
end
|
|
80
76
|
end
|
|
81
77
|
end
|
data/lib/suma/schema_comparer.rb
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "tmpdir"
|
|
5
|
-
require_relative "eengine/wrapper"
|
|
6
|
-
require_relative "eengine_converter"
|
|
7
5
|
|
|
8
6
|
module Suma
|
|
9
7
|
class SchemaComparer
|
|
@@ -37,7 +35,10 @@ module Suma
|
|
|
37
35
|
return nil
|
|
38
36
|
end
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
unless result[:xml_path]
|
|
39
|
+
raise Suma::CompilationError,
|
|
40
|
+
"XML output not found"
|
|
41
|
+
end
|
|
41
42
|
|
|
42
43
|
convert_to_change_yaml(result[:xml_path], out_dir)
|
|
43
44
|
ensure
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "pathname"
|
|
5
|
+
|
|
6
|
+
module Suma
|
|
7
|
+
# Orchestrates the compilation of a single EXPRESS schema into HTML/XML
|
|
8
|
+
# via Metanorma.
|
|
9
|
+
#
|
|
10
|
+
# SchemaCompiler owns all file I/O and the Metanorma::Compile invocation
|
|
11
|
+
# for one schema; the rendered AsciiDoc body is supplied by a SchemaTemplate
|
|
12
|
+
# that the caller injects. This split means templates can be tested as pure
|
|
13
|
+
# functions and the compiler can be tested with a real adoc fixture, without
|
|
14
|
+
# either depending on the other.
|
|
15
|
+
class SchemaCompiler
|
|
16
|
+
attr_reader :schema, :id, :output_path, :template
|
|
17
|
+
|
|
18
|
+
def initialize(schema:, output_path:, template:)
|
|
19
|
+
@schema = schema
|
|
20
|
+
@id = schema.id
|
|
21
|
+
@output_path = output_path
|
|
22
|
+
@template = template
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def compile
|
|
26
|
+
save_config
|
|
27
|
+
save_adoc
|
|
28
|
+
invoke_metanorma
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def output_xml_path
|
|
33
|
+
filename_adoc("xml")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def extensions
|
|
37
|
+
template.extensions
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def filename_adoc(ext = "adoc")
|
|
43
|
+
File.join(output_path, "doc_#{id}.#{ext}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def filename_config
|
|
47
|
+
File.join(output_path, "schema_#{id}.yaml")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def save_adoc
|
|
51
|
+
log_relative "Save EXPRESS adoc", filename_adoc
|
|
52
|
+
|
|
53
|
+
FileUtils.mkdir_p(File.dirname(filename_adoc))
|
|
54
|
+
|
|
55
|
+
config_relative = Pathname.new(filename_config)
|
|
56
|
+
.relative_path_from(Pathname.new(File.dirname(filename_adoc)))
|
|
57
|
+
|
|
58
|
+
File.write(filename_adoc, template.render(config_relative))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def save_config
|
|
62
|
+
log_relative "Save schema config", filename_config
|
|
63
|
+
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(filename_config))
|
|
65
|
+
config = Expressir::SchemaManifest.new
|
|
66
|
+
config.schemas << Expressir::SchemaManifestEntry.new(id: id,
|
|
67
|
+
path: schema.path)
|
|
68
|
+
config.save_to_path(filename_config)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def invoke_metanorma
|
|
72
|
+
log_relative "Compiling schema (id: #{id})", filename_adoc
|
|
73
|
+
Metanorma::Compile.new.compile(
|
|
74
|
+
filename_adoc,
|
|
75
|
+
agree_to_terms: true,
|
|
76
|
+
install_fonts: false,
|
|
77
|
+
)
|
|
78
|
+
log_relative "Compiling schema (id: #{id}) ... done!", filename_adoc
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def log_relative(prefix, path)
|
|
82
|
+
relative = Pathname.new(path).relative_path_from(Dir.pwd)
|
|
83
|
+
Utils.log "#{prefix}: #{relative}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Suma
|
|
4
|
+
# Service for schema-config I/O on a single CollectionManifest node.
|
|
5
|
+
#
|
|
6
|
+
# SchemaDiscovery owns two concerns that previously lived on
|
|
7
|
+
# CollectionManifest:
|
|
8
|
+
#
|
|
9
|
+
# 1. Loading the `schemas.yaml` that sits next to a `collection.yml`
|
|
10
|
+
# into the manifest's `schema_config` slot.
|
|
11
|
+
# 2. Building the doc CollectionManifest sub-tree that hosts the
|
|
12
|
+
# compiled XML output for each schema in `schema_config`.
|
|
13
|
+
#
|
|
14
|
+
# It does not walk the manifest tree — that is ManifestTraverser's job.
|
|
15
|
+
# The split keeps schema I/O in one place and tree traversal in another,
|
|
16
|
+
# so each is independently testable.
|
|
17
|
+
class SchemaDiscovery
|
|
18
|
+
attr_reader :manifest
|
|
19
|
+
|
|
20
|
+
def initialize(manifest)
|
|
21
|
+
@manifest = manifest
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# If the manifest's file is a `collection.yml` and a `schemas.yaml`
|
|
25
|
+
# sits alongside it, parse it into an Expressir::SchemaManifest and
|
|
26
|
+
# store it on the manifest. Otherwise leave `schema_config` untouched.
|
|
27
|
+
def load_config
|
|
28
|
+
return unless manifest.file
|
|
29
|
+
return unless File.basename(manifest.file) == "collection.yml"
|
|
30
|
+
|
|
31
|
+
schemas_yaml_path = File.join(File.dirname(manifest.file), "schemas.yaml")
|
|
32
|
+
return unless File.exist?(schemas_yaml_path)
|
|
33
|
+
|
|
34
|
+
manifest.schema_config = Expressir::SchemaManifest.from_file(schemas_yaml_path)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Build a CollectionManifest sub-tree that hosts the compiled XML for
|
|
38
|
+
# every schema in `manifest.schema_config`. The wrapper is named with
|
|
39
|
+
# a trailing underscore so it does not collide with the parent id.
|
|
40
|
+
def build_added_manifest(schema_output_path)
|
|
41
|
+
doc = CollectionConfig.from_file(manifest.file)
|
|
42
|
+
doc_id = doc.bibdata.id
|
|
43
|
+
|
|
44
|
+
added = CollectionManifest.new(
|
|
45
|
+
title: "Collection",
|
|
46
|
+
type: "collection",
|
|
47
|
+
identifier: "#{manifest.identifier}_",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
added.entry = [
|
|
51
|
+
CollectionManifest.new(
|
|
52
|
+
title: doc_id,
|
|
53
|
+
type: "document",
|
|
54
|
+
entry: build_doc_entries(schema_output_path),
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
added
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Build one CollectionManifest per schema in schema_config, naming
|
|
62
|
+
# each output file as `doc_<basename>.xml` under
|
|
63
|
+
# `<schema_output_path>/<schema_id>/`.
|
|
64
|
+
def build_doc_entries(schema_output_path)
|
|
65
|
+
manifest.schema_config.schemas.map do |schema|
|
|
66
|
+
xml_basename = "#{File.basename(schema.path, '.exp')}.xml"
|
|
67
|
+
CollectionManifest.new(
|
|
68
|
+
identifier: schema.id,
|
|
69
|
+
title: schema.id,
|
|
70
|
+
file: File.join(schema_output_path, schema.id, "doc_#{xml_basename}"),
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
data/lib/suma/schema_exporter.rb
CHANGED
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "express_schema"
|
|
4
|
-
require_relative "utils"
|
|
5
3
|
require "fileutils"
|
|
6
4
|
|
|
7
5
|
module Suma
|
|
8
6
|
# SchemaExporter exports EXPRESS schemas from a manifest
|
|
9
7
|
# with configurable options for annotations and ZIP packaging
|
|
10
8
|
class SchemaExporter
|
|
11
|
-
CATEGORY_MAP = {
|
|
12
|
-
ExpressSchema::Type::RESOURCE => "resources",
|
|
13
|
-
ExpressSchema::Type::MODULE_ARM => "modules",
|
|
14
|
-
ExpressSchema::Type::MODULE_MIM => "modules",
|
|
15
|
-
ExpressSchema::Type::BUSINESS_OBJECT_MODEL => "business_object_models",
|
|
16
|
-
ExpressSchema::Type::CORE_MODEL => "core_model",
|
|
17
|
-
ExpressSchema::Type::STANDALONE => ".",
|
|
18
|
-
}.freeze
|
|
19
|
-
|
|
20
9
|
attr_reader :schemas, :output_path, :options
|
|
21
10
|
|
|
22
11
|
def initialize(schemas:, output_path:, options: {})
|
|
@@ -68,16 +57,11 @@ module Suma
|
|
|
68
57
|
if is_standalone
|
|
69
58
|
output_path.to_s
|
|
70
59
|
else
|
|
71
|
-
category =
|
|
72
|
-
output_path.join(category).to_s
|
|
60
|
+
category = SchemaCategory.for_schema(id: schema.id, path: schema.path)
|
|
61
|
+
output_path.join(category.directory).to_s
|
|
73
62
|
end
|
|
74
63
|
end
|
|
75
64
|
|
|
76
|
-
def categorize_schema(schema)
|
|
77
|
-
type = ExpressSchema::Type.classify(id: schema.id, path: schema.path)
|
|
78
|
-
CATEGORY_MAP.fetch(type, "standalone")
|
|
79
|
-
end
|
|
80
|
-
|
|
81
65
|
# rubocop:disable Metrics/MethodLength
|
|
82
66
|
def create_zip_archive
|
|
83
67
|
require "zip"
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
require "pathname"
|
|
5
|
-
require_relative "utils"
|
|
6
5
|
|
|
7
6
|
module Suma
|
|
8
7
|
class SchemaManifestGenerator
|
|
9
8
|
YAML_FILE_EXTENSIONS = [".yaml", ".yml"].freeze
|
|
10
9
|
|
|
11
|
-
def initialize(metanorma_manifest_file, schema_manifest_file,
|
|
10
|
+
def initialize(metanorma_manifest_file, schema_manifest_file,
|
|
11
|
+
exclude_paths: nil)
|
|
12
12
|
@metanorma_manifest_file = File.expand_path(metanorma_manifest_file)
|
|
13
13
|
@schema_manifest_file = schema_manifest_file
|
|
14
14
|
@exclude_paths = exclude_paths
|
|
@@ -27,9 +27,15 @@ module Suma
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
29
|
def validate_inputs
|
|
30
|
-
|
|
30
|
+
unless File.exist?(@metanorma_manifest_file)
|
|
31
|
+
raise Errno::ENOENT,
|
|
32
|
+
"Specified file `#{@metanorma_manifest_file}` not found."
|
|
33
|
+
end
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
unless File.file?(@metanorma_manifest_file)
|
|
36
|
+
raise ArgumentError,
|
|
37
|
+
"Specified path `#{@metanorma_manifest_file}` is not a file."
|
|
38
|
+
end
|
|
33
39
|
|
|
34
40
|
[@metanorma_manifest_file, @schema_manifest_file].each do |file|
|
|
35
41
|
unless YAML_FILE_EXTENSIONS.include?(File.extname(file))
|
|
@@ -53,7 +59,8 @@ module Suma
|
|
|
53
59
|
all_schemas = { "schemas" => {} }
|
|
54
60
|
|
|
55
61
|
manifest_files.each do |file|
|
|
56
|
-
schemas_file_path = File.expand_path(file.gsub("collection.yml",
|
|
62
|
+
schemas_file_path = File.expand_path(file.gsub("collection.yml",
|
|
63
|
+
"schemas.yaml"))
|
|
57
64
|
|
|
58
65
|
unless File.exist?(schemas_file_path)
|
|
59
66
|
Utils.log "Schemas file not found: #{schemas_file_path}"
|
|
@@ -83,7 +90,8 @@ module Suma
|
|
|
83
90
|
schema_manifest_path = File.expand_path(@schema_manifest_file, Dir.pwd)
|
|
84
91
|
|
|
85
92
|
schemas_data["schemas"].each do |key, value|
|
|
86
|
-
path_in_schema = File.expand_path(value["path"],
|
|
93
|
+
path_in_schema = File.expand_path(value["path"],
|
|
94
|
+
File.dirname(schemas_file_path))
|
|
87
95
|
|
|
88
96
|
fixed_path = Pathname.new(path_in_schema).relative_path_from(
|
|
89
97
|
Pathname.new(File.dirname(schema_manifest_path)),
|