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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -1
  3. data/.rubocop_todo.yml +170 -13
  4. data/CLAUDE.md +37 -11
  5. data/Gemfile +3 -3
  6. data/README.adoc +57 -1
  7. data/exe/suma +1 -1
  8. data/lib/suma/cli/build.rb +0 -5
  9. data/lib/suma/cli/check_svg_quality.rb +0 -6
  10. data/lib/suma/cli/compare.rb +0 -1
  11. data/lib/suma/cli/convert_jsdai.rb +0 -2
  12. data/lib/suma/cli/core.rb +119 -0
  13. data/lib/suma/cli/export.rb +0 -3
  14. data/lib/suma/cli/extract_terms.rb +5 -8
  15. data/lib/suma/cli/generate_register.rb +34 -0
  16. data/lib/suma/cli/generate_schemas.rb +0 -2
  17. data/lib/suma/cli/reformat.rb +0 -1
  18. data/lib/suma/cli/validate.rb +0 -2
  19. data/lib/suma/cli/validate_links.rb +0 -2
  20. data/lib/suma/cli.rb +12 -141
  21. data/lib/suma/collection_config.rb +0 -2
  22. data/lib/suma/collection_manifest.rb +7 -110
  23. data/lib/suma/eengine/wrapper.rb +0 -1
  24. data/lib/suma/eengine.rb +8 -0
  25. data/lib/suma/express_schema.rb +0 -1
  26. data/lib/suma/jsdai/figure.rb +0 -3
  27. data/lib/suma/jsdai.rb +5 -2
  28. data/lib/suma/link_validator.rb +15 -7
  29. data/lib/suma/manifest_traverser.rb +92 -0
  30. data/lib/suma/processor.rb +5 -8
  31. data/lib/suma/register_manifest_generator.rb +163 -0
  32. data/lib/suma/schema_category.rb +83 -0
  33. data/lib/suma/schema_collection.rb +29 -33
  34. data/lib/suma/schema_comparer.rb +4 -3
  35. data/lib/suma/schema_compiler.rb +86 -0
  36. data/lib/suma/schema_discovery.rb +75 -0
  37. data/lib/suma/schema_exporter.rb +2 -18
  38. data/lib/suma/schema_manifest_generator.rb +14 -6
  39. data/lib/suma/schema_naming.rb +111 -0
  40. data/lib/suma/schema_template/document.rb +141 -0
  41. data/lib/suma/schema_template/plain.rb +46 -0
  42. data/lib/suma/schema_template.rb +19 -0
  43. data/lib/suma/svg_quality/batch_report.rb +0 -2
  44. data/lib/suma/svg_quality/formatters.rb +12 -0
  45. data/lib/suma/svg_quality.rb +3 -1
  46. data/lib/suma/term_extractor.rb +119 -46
  47. data/lib/suma/urn.rb +61 -0
  48. data/lib/suma/version.rb +1 -1
  49. data/lib/suma.rb +31 -3
  50. data/suma.gemspec +1 -1
  51. metadata +24 -6
  52. data/lib/suma/schema_attachment.rb +0 -103
  53. data/lib/suma/schema_document.rb +0 -118
@@ -1,28 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../thor_ext"
5
- require_relative "../term_extractor"
6
4
 
7
5
  module Suma
8
6
  module Cli
9
7
  class ExtractTerms < Thor
10
8
  desc "extract_terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH",
11
9
  "Extract terms from SCHEMA_MANIFEST_FILE into " \
12
- "Glossarist v2 format"
10
+ "Glossarist v3 format"
13
11
  option :language_code, type: :string, default: "eng", aliases: "-l",
14
12
  desc: "Language code for the Glossarist"
13
+ option :urn, type: :string, required: true, aliases: "-u",
14
+ desc: "URN for the dataset source " \
15
+ "(used for section references)"
15
16
 
16
17
  def extract_terms(schema_manifest_file, output_path)
17
- unless File.exist?(File.expand_path(schema_manifest_file))
18
- raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
19
- "`#{schema_manifest_file}` not found."
20
- end
21
-
22
18
  TermExtractor.new(
23
19
  schema_manifest_file,
24
20
  output_path,
25
21
  language_code: options[:language_code],
22
+ urn: options[:urn],
26
23
  ).call
27
24
  end
28
25
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+
5
+ module Suma
6
+ module Cli
7
+ class GenerateRegister < Thor
8
+ desc "generate_register SCHEMA_MANIFEST_FILE OUTPUT_PATH",
9
+ "Generate a Glossarist register.yaml with hierarchical sections"
10
+ option :language_code, type: :string, default: "eng", aliases: "-l",
11
+ desc: "Language code for section names"
12
+ option :urn, type: :string, required: true, aliases: "-u",
13
+ desc: "URN prefix for the dataset"
14
+ option :id, type: :string, required: true,
15
+ desc: "Dataset identifier (e.g. iso10303-2-express)"
16
+ option :ref, type: :string, required: true,
17
+ desc: "Human-readable reference label"
18
+ option :owner, type: :string, default: Suma::RegisterManifestGenerator::DEFAULT_OWNER,
19
+ desc: "Owner of the dataset (e.g. 'ISO/TC 184/SC 4')"
20
+
21
+ def generate_register(schema_manifest_file, output_path)
22
+ RegisterManifestGenerator.new(
23
+ schema_manifest_file,
24
+ output_path,
25
+ urn: options[:urn],
26
+ id: options[:id],
27
+ ref: options[:ref],
28
+ language_code: options[:language_code],
29
+ owner: options[:owner],
30
+ ).generate
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../thor_ext"
5
- require_relative "../schema_manifest_generator"
6
4
 
7
5
  module Suma
8
6
  module Cli
@@ -1,7 +1,6 @@
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
@@ -9,8 +9,6 @@ module Suma
9
9
  desc "links SCHEMAS_FILE DOCUMENTS_PATH [OUTPUT_FILE]",
10
10
  "Extract and validate express links without creating intermediate file"
11
11
  def links(*args)
12
- require_relative "validate_links"
13
-
14
12
  # Forward the command to ValidateLinks
15
13
  links = Cli::ValidateLinks.new
16
14
  links.extract_and_validate(*args)
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "../utils"
5
- require_relative "../link_validator"
6
4
  require "expressir"
7
5
 
8
6
  module Suma
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
- # Core command class for handling CLI entrypoints
13
- class Core < Thor
14
- extend ThorExt::Start
15
-
16
- desc "build METANORMA_SITE_MANIFEST",
17
- "Build collection specified in site manifest (`metanorma*.yml`)"
18
- option :compile, type: :boolean, default: true,
19
- desc: "Compile or skip compile of collection"
20
- option :schemas_all_path, type: :string, aliases: "-s",
21
- desc: "Generate file that contains all " \
22
- "schemas in the collection."
23
- def build(_site_manifest)
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
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "utils"
4
3
  require "lutaml/model"
5
- require_relative "collection_manifest"
6
4
  require "metanorma"
7
5
 
8
6
  module Suma
@@ -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
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "open3"
4
- require_relative "errors"
5
4
 
6
5
  module Suma
7
6
  module Eengine
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Suma
4
+ module Eengine
5
+ autoload :Errors, "suma/eengine/errors"
6
+ autoload :Wrapper, "suma/eengine/wrapper"
7
+ end
8
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "utils"
4
3
  require "fileutils"
5
4
  require "expressir"
6
5
 
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "figure_xml"
4
- require_relative "figure_image"
5
-
6
3
  module Suma
7
4
  module Jsdai
8
5
  # Main class for JSDAI figure conversion
data/lib/suma/jsdai.rb CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Suma
2
4
  module Jsdai
5
+ autoload :Figure, "suma/jsdai/figure"
6
+ autoload :FigureImage, "suma/jsdai/figure_image"
7
+ autoload :FigureXml, "suma/jsdai/figure_xml"
3
8
  end
4
9
  end
5
-
6
- require_relative "jsdai/figure"
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "schema_index"
4
3
  require "expressir"
5
4
 
6
5
  module Suma
7
- LinkValidationResult = Struct.new(:file, :line, :link, :reason, keyword_init: true)
6
+ LinkValidationResult = Struct.new(:file, :line, :link, :reason,
7
+ keyword_init: true)
8
8
 
9
9
  class LinkValidator
10
10
  def initialize(index)
@@ -93,18 +93,22 @@ module Suma
93
93
 
94
94
  return unless parts.size > 2
95
95
 
96
- error = validate_deep_path(schema, element, parts[2..], file, line_idx, link)
96
+ error = validate_deep_path(schema, element, parts[2..], file, line_idx,
97
+ link)
97
98
  unresolved << error if error
98
99
  end
99
100
 
100
- def validate_deep_path(schema, element, path_parts, file, line_idx, full_link)
101
+ def validate_deep_path(schema, element, path_parts, file, line_idx,
102
+ full_link)
101
103
  current = element
102
104
  current_path = "#{schema.id}.#{element.id}"
103
105
 
104
106
  path_parts.each do |part|
105
107
  case current
106
108
  when Expressir::Model::Declarations::Entity
107
- attribute = current.attributes&.find { |a| a.id.downcase == part.downcase }
109
+ attribute = current.attributes&.find do |a|
110
+ a.id.downcase == part.downcase
111
+ end
108
112
 
109
113
  unless attribute
110
114
  return LinkValidationResult.new(
@@ -122,7 +126,9 @@ module Suma
122
126
  underlying = current.underlying_type
123
127
 
124
128
  if underlying.is_a?(Expressir::Model::DataTypes::Enumeration)
125
- enum_value = underlying.items.find { |e| e.id.downcase == part.downcase }
129
+ enum_value = underlying.items.find do |e|
130
+ e.id.downcase == part.downcase
131
+ end
126
132
 
127
133
  unless enum_value
128
134
  return LinkValidationResult.new(
@@ -193,7 +199,9 @@ module Suma
193
199
  schema.procedures,
194
200
  schema.subtype_constraints,
195
201
  ].each do |collection|
196
- element = collection&.find { |e| e.id.downcase == element_name.downcase }
202
+ element = collection&.find do |e|
203
+ e.id.downcase == element_name.downcase
204
+ end
197
205
  return element if element
198
206
  end
199
207
 
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "expressir"
4
+
5
+ module Suma
6
+ # Tree-walking service over a CollectionManifest.
7
+ #
8
+ # ManifestTraverser owns all imperative operations that walk or mutate a
9
+ # manifest tree: finding schemas-only entries, expanding them into
10
+ # compiled-doc sub-trees, exporting a unified Expressir::SchemaManifest,
11
+ # and removing schemas-only sources after compilation.
12
+ #
13
+ # Each method takes the manifest passed at construction as its root;
14
+ # recursion instantiates a new traverser per child node so the surface
15
+ # stays instance-based and the data model (CollectionManifest) stays pure.
16
+ #
17
+ # Schema-config loading and doc-entry construction are delegated to
18
+ # SchemaDiscovery so this class owns traversal, not schema I/O.
19
+ class ManifestTraverser
20
+ attr_reader :manifest
21
+
22
+ def initialize(manifest)
23
+ @manifest = manifest
24
+ end
25
+
26
+ # Returns every entry (anywhere in the tree) whose `schemas_only` flag
27
+ # is truthy, plus +manifest+ itself if it is schemas-only.
28
+ def find_schemas_only
29
+ results = (manifest.entry || []).select(&:schemas_only)
30
+ results << manifest if manifest.schemas_only
31
+ results
32
+ end
33
+
34
+ # Recursively concatenate every nested schema_config into a single
35
+ # Expressir::SchemaManifest. The manifest's own schema_config (if set)
36
+ # seeds the result; otherwise an empty SchemaManifest is returned.
37
+ def export_schema_config(path)
38
+ export_config = manifest.schema_config || Expressir::SchemaManifest.new
39
+ return export_config unless manifest.entry
40
+
41
+ manifest.entry.each do |child|
42
+ child_config = self.class.new(child).export_schema_config(path)
43
+ export_config.concat(child_config) if child_config
44
+ end
45
+
46
+ export_config
47
+ end
48
+
49
+ # Expand schemas-only entries into compiled-doc sub-trees. If the
50
+ # manifest has no file, walks children via process_entry. Otherwise
51
+ # loads the schema_config (SchemaDiscovery), and if this entry is
52
+ # schemas-only, hides it from the output index and appends a new
53
+ # sub-collection that hosts the compiled docs.
54
+ def expand_schemas_only(schema_output_path)
55
+ return process_entry(schema_output_path) unless manifest.file
56
+
57
+ SchemaDiscovery.new(manifest).load_config
58
+
59
+ return process_entry(schema_output_path) unless manifest.schemas_only
60
+
61
+ manifest.index = false
62
+
63
+ added = SchemaDiscovery.new(manifest).build_added_manifest(schema_output_path)
64
+ [manifest, added]
65
+ end
66
+
67
+ # Drop every direct child whose `schemas_only` is truthy. Called after
68
+ # compilation so the schemas-only manifest file no longer shows up in
69
+ # the rendered collection.
70
+ def remove_schemas_only_sources
71
+ return unless manifest.entry
72
+
73
+ kept = manifest.entry.reject(&:schemas_only)
74
+ manifest.entry = kept
75
+ end
76
+
77
+ private
78
+
79
+ # Walk each child via expand_schemas_only, flattening the results back
80
+ # into manifest.entry.
81
+ def process_entry(schema_output_path)
82
+ return [manifest] unless manifest.entry
83
+
84
+ flattened = manifest.entry.each_with_object([]) do |child, acc|
85
+ acc.concat(self.class.new(child).expand_schemas_only(schema_output_path))
86
+ end
87
+
88
+ manifest.entry = flattened
89
+ [manifest]
90
+ end
91
+ end
92
+ end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "schema_collection"
4
- require_relative "utils"
5
- require_relative "collection_config"
6
- require_relative "site_config"
7
3
  require "metanorma"
8
4
 
9
5
  module Suma
@@ -41,11 +37,12 @@ module Suma
41
37
  collection_config_path = site_config.metanorma.source.files.first
42
38
  collection_config = Suma::CollectionConfig.from_file(collection_config_path)
43
39
  collection_config.path = collection_config_path
44
- collection_config.manifest.expand_schemas_only("schema_docs")
45
40
 
46
- exported_schema_config = collection_config.manifest.export_schema_config(schemas_all_path)
47
- exported_schema_config.path = schemas_all_path
41
+ traverser = ManifestTraverser.new(collection_config.manifest)
42
+ traverser.expand_schemas_only("schema_docs")
48
43
 
44
+ exported_schema_config = traverser.export_schema_config(schemas_all_path)
45
+ exported_schema_config.path = schemas_all_path
49
46
  exported_schema_config.to_file
50
47
 
51
48
  collection_config
@@ -72,7 +69,7 @@ module Suma
72
69
 
73
70
  def build_collection(collection_config, output_directory)
74
71
  new_collection_config_path = "collection-output.yaml"
75
- collection_config.manifest.remove_schemas_only_sources
72
+ ManifestTraverser.new(collection_config.manifest).remove_schemas_only_sources
76
73
  collection_config.to_file(new_collection_config_path)
77
74
 
78
75
  collection = Metanorma::Collection.parse(new_collection_config_path)