suma 0.2.5 → 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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -0
  3. data/.github/workflows/release.yml +5 -1
  4. data/.gitignore +10 -1
  5. data/.rubocop_todo.yml +237 -28
  6. data/CLAUDE.md +102 -0
  7. data/Gemfile +3 -1
  8. data/README.adoc +188 -1
  9. data/exe/suma +1 -1
  10. data/lib/suma/cli/build.rb +2 -8
  11. data/lib/suma/cli/check_svg_quality.rb +172 -0
  12. data/lib/suma/cli/compare.rb +6 -158
  13. data/lib/suma/cli/convert_jsdai.rb +0 -2
  14. data/lib/suma/cli/core.rb +119 -0
  15. data/lib/suma/cli/export.rb +1 -10
  16. data/lib/suma/cli/extract_terms.rb +10 -654
  17. data/lib/suma/cli/generate_register.rb +34 -0
  18. data/lib/suma/cli/generate_schemas.rb +8 -124
  19. data/lib/suma/cli/reformat.rb +0 -1
  20. data/lib/suma/cli/validate.rb +0 -2
  21. data/lib/suma/cli/validate_links.rb +14 -291
  22. data/lib/suma/cli.rb +12 -102
  23. data/lib/suma/collection_config.rb +0 -2
  24. data/lib/suma/collection_manifest.rb +7 -111
  25. data/lib/suma/eengine/wrapper.rb +0 -1
  26. data/lib/suma/eengine.rb +8 -0
  27. data/lib/suma/express_schema.rb +43 -31
  28. data/lib/suma/jsdai/figure.rb +0 -3
  29. data/lib/suma/jsdai/figure_xml.rb +12 -9
  30. data/lib/suma/jsdai.rb +5 -8
  31. data/lib/suma/link_validator.rb +211 -0
  32. data/lib/suma/manifest_traverser.rb +92 -0
  33. data/lib/suma/processor.rb +76 -105
  34. data/lib/suma/register_manifest_generator.rb +163 -0
  35. data/lib/suma/schema_category.rb +83 -0
  36. data/lib/suma/schema_collection.rb +28 -63
  37. data/lib/suma/schema_comparer.rb +117 -0
  38. data/lib/suma/schema_compiler.rb +86 -0
  39. data/lib/suma/schema_discovery.rb +75 -0
  40. data/lib/suma/schema_exporter.rb +7 -35
  41. data/lib/suma/schema_index.rb +53 -0
  42. data/lib/suma/schema_manifest_generator.rb +113 -0
  43. data/lib/suma/schema_naming.rb +111 -0
  44. data/lib/suma/schema_template/document.rb +141 -0
  45. data/lib/suma/schema_template/plain.rb +46 -0
  46. data/lib/suma/schema_template.rb +19 -0
  47. data/lib/suma/svg_quality/batch_report.rb +78 -0
  48. data/lib/suma/svg_quality/formatters/json_formatter.rb +30 -0
  49. data/lib/suma/svg_quality/formatters/terminal_formatter.rb +168 -0
  50. data/lib/suma/svg_quality/formatters/yaml_formatter.rb +32 -0
  51. data/lib/suma/svg_quality/formatters.rb +12 -0
  52. data/lib/suma/svg_quality/report.rb +52 -0
  53. data/lib/suma/svg_quality.rb +30 -0
  54. data/lib/suma/term_extractor.rb +466 -0
  55. data/lib/suma/urn.rb +61 -0
  56. data/lib/suma/utils.rb +10 -2
  57. data/lib/suma/version.rb +1 -1
  58. data/lib/suma.rb +34 -5
  59. data/suma.gemspec +3 -2
  60. metadata +53 -9
  61. data/lib/suma/export_standalone_schema.rb +0 -14
  62. data/lib/suma/schema_attachment.rb +0 -130
  63. data/lib/suma/schema_document.rb +0 -132
@@ -0,0 +1,466 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "expressir"
5
+ require "glossarist"
6
+
7
+ module Suma
8
+ class TermExtractor
9
+ REDUNDANT_NOTE_REGEX =
10
+ %r{
11
+ ^An? # Starts with "A" or "An"
12
+ \s.*?\sis\sa\stype\sof # Text followed by "is a type of"
13
+ (\sa|\san)? # Optional " a" or " an"
14
+ \s\{\{[^\}]*\}\} # Text in double curly braces
15
+ \s*?\.?$ # Optional whitespace and period at the end
16
+ }x
17
+
18
+ def initialize(schema_manifest_file, output_path, urn:,
19
+ language_code: "eng")
20
+ @schema_manifest_file = File.expand_path(schema_manifest_file)
21
+ @output_path = output_path
22
+ @language_code = language_code
23
+ @urn = Suma::Urn.new(urn)
24
+ end
25
+
26
+ def call
27
+ validate_inputs
28
+ get_exp_files.map do |exp_file|
29
+ extract(exp_file)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def validate_inputs
36
+ unless File.exist?(@schema_manifest_file)
37
+ raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
38
+ "`#{@schema_manifest_file}` not found."
39
+ end
40
+ unless File.file?(@schema_manifest_file)
41
+ raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
42
+ "`#{@schema_manifest_file}` is not a file."
43
+ end
44
+ end
45
+
46
+ def get_exp_files
47
+ config = Expressir::SchemaManifest.from_file(@schema_manifest_file)
48
+ paths = config.schemas.map(&:path)
49
+
50
+ if paths.empty?
51
+ raise Errno::ENOENT,
52
+ "No EXPRESS files found in `#{@schema_manifest_file}`."
53
+ end
54
+
55
+ paths
56
+ end
57
+
58
+ def extract(exp_file)
59
+ exp_path_rel = Pathname.new(exp_file).relative_path_from(Pathname.getwd)
60
+ Utils.log "Building terms: #{exp_path_rel}"
61
+
62
+ repo = Expressir::Express::Parser.from_file(exp_file)
63
+ schema = repo.schemas.first
64
+
65
+ raise Error, "Schema must have an associated file" unless schema.file
66
+
67
+ collection = build_managed_concept_collection(schema)
68
+ output_data(collection)
69
+ collection
70
+ end
71
+
72
+ def output_data(collection)
73
+ output_dir = File.expand_path(@output_path)
74
+ FileUtils.mkdir_p(output_dir)
75
+ Utils.log "Saving collection to files in: #{@output_path}"
76
+
77
+ collection.each do |concept|
78
+ doc = Glossarist::V3::ConceptDocument.from_managed_concept(concept)
79
+ doc.localizations = concept.data.localizations.keys.map do |lang|
80
+ concept.localization(lang)
81
+ end
82
+
83
+ filename = "#{concept.uuid.gsub(/[^\w.-]/, '_')}.yaml"
84
+ File.write(File.join(output_dir, filename), doc.to_yamls,
85
+ encoding: "utf-8")
86
+ end
87
+ end
88
+
89
+ def build_managed_concept_collection(schema)
90
+ source_ref = get_source_ref(schema)
91
+ section_ref = get_section_ref(schema)
92
+
93
+ Glossarist::ManagedConceptCollection.new.tap do |collection|
94
+ schema.entities.each do |entity|
95
+ localized_concept_id = Glossarist::Utilities::UUID.uuid_v5(
96
+ Glossarist::Utilities::UUID::OID_NAMESPACE,
97
+ "#{schema.id}.#{entity.id}-#{@language_code}",
98
+ )
99
+
100
+ localized_concept = build_localized_concept(
101
+ schema: schema,
102
+ entity: entity,
103
+ source_ref: source_ref,
104
+ uuid: localized_concept_id,
105
+ )
106
+
107
+ managed_data = Glossarist::V3::ManagedConceptData.new.tap do |data|
108
+ data.id = "#{schema.id}.#{entity.id}"
109
+ data.localizations.store(@language_code, localized_concept)
110
+ data.localized_concepts = { @language_code => localized_concept_id }
111
+ data.domains = [section_ref] if section_ref
112
+ end
113
+
114
+ managed_concept = Glossarist::V3::ManagedConcept.new.tap do |concept|
115
+ concept.id = managed_data.id
116
+ concept.uuid = Glossarist::Utilities::UUID.uuid_v5(
117
+ Glossarist::Utilities::UUID::OID_NAMESPACE,
118
+ managed_data.id,
119
+ )
120
+ concept.data = managed_data
121
+ concept.schema_version = "v3"
122
+ end
123
+
124
+ collection.store(managed_concept)
125
+ end
126
+ end
127
+ end
128
+
129
+ def build_localized_concept(schema:, entity:, source_ref:, uuid:)
130
+ schema_domain = get_domain(schema)
131
+
132
+ localized_concept_data = Glossarist::V3::ConceptData.new.tap do |data|
133
+ data.terms = get_entity_terms(entity)
134
+ data.definition = get_entity_definitions(entity, schema)
135
+ data.language_code = @language_code
136
+ data.domain = schema_domain
137
+ data.sources = [source_ref] if source_ref
138
+
139
+ notes = get_entity_notes(entity, schema_domain, data.definition)
140
+ data.notes = notes if notes && !notes.empty?
141
+ data.examples = []
142
+ end
143
+
144
+ Glossarist::V3::LocalizedConcept.new(
145
+ data: localized_concept_data, uuid: uuid,
146
+ )
147
+ end
148
+
149
+ def schema_urn(schema)
150
+ @urn.for_schema(schema.id)
151
+ end
152
+
153
+ def term_urn(concept_identifier)
154
+ @urn.for_term(concept_identifier)
155
+ end
156
+
157
+ def express_entity_urn(full_ref)
158
+ @urn.for_entity(full_ref)
159
+ end
160
+
161
+ def urn_mention(urn, display)
162
+ "{{#{urn},#{display}}}"
163
+ end
164
+
165
+ def get_section_ref(schema)
166
+ return nil unless @urn
167
+
168
+ Glossarist::ConceptReference.new(
169
+ concept_id: "section-#{schema.id}",
170
+ source: schema_urn(schema),
171
+ ref_type: "section",
172
+ )
173
+ end
174
+
175
+ def get_source_ref(schema)
176
+ ref = Glossarist::Citation::Ref.new
177
+ ref.source = schema_urn(schema)
178
+
179
+ build_custom_locality(schema).each do |cl|
180
+ case cl.name
181
+ when "version" then ref.version = cl.value
182
+ end
183
+ end
184
+
185
+ origin = Glossarist::V3::Citation.new
186
+ origin.ref = ref
187
+ Glossarist::V3::ConceptSource.new(id: schema.id, type: "authoritative",
188
+ origin: origin)
189
+ end
190
+
191
+ def build_custom_locality(schema)
192
+ localities = []
193
+
194
+ version_item = schema.version.items.detect { |i| i.name == "version" }
195
+ if version_item
196
+ localities << Glossarist::CustomLocality.new(name: "version",
197
+ value: version_item.value)
198
+ end
199
+
200
+ localities
201
+ end
202
+
203
+ def get_domain(schema)
204
+ prefix = if schema.id.end_with?("_arm", "_mim")
205
+ "application module"
206
+ else
207
+ "resource"
208
+ end
209
+
210
+ "#{prefix}: #{schema.id}"
211
+ end
212
+
213
+ def get_entity_terms(entity)
214
+ [
215
+ Glossarist::Designation::Base.new(
216
+ designation: entity.id,
217
+ type: "expression",
218
+ normative_status: "preferred",
219
+ ),
220
+ ]
221
+ end
222
+
223
+ def get_entity_definitions(entity, schema)
224
+ schema_type = extract_file_type(schema.file)
225
+ get_domain(schema)
226
+
227
+ definition = generate_entity_definition(entity, schema, schema_type)
228
+ [Glossarist::V3::DetailedDefinition.new(content: definition)]
229
+ end
230
+
231
+ def get_entity_notes(entity, schema_domain, definitions)
232
+ notes = []
233
+
234
+ if entity.remarks && !entity.remarks.empty?
235
+ trimmed_def = trim_definition(entity.remarks)
236
+ if trimmed_def && !trimmed_def.empty?
237
+ notes << Glossarist::V3::DetailedDefinition.new(
238
+ content: convert_express_xref(trimmed_def, schema_domain),
239
+ )
240
+ end
241
+ end
242
+
243
+ notes = only_keep_first_sentence(notes)
244
+ notes = remove_see_content(notes)
245
+ notes = remove_redundant_note(notes)
246
+ notes = remove_invalid_references(notes)
247
+ compare_with_definitions(notes, definitions)
248
+ end
249
+
250
+ def only_keep_first_sentence(notes)
251
+ notes.each do |note|
252
+ if note&.content && should_preserve_complete_structure?(note.content)
253
+ next
254
+ end
255
+
256
+ if note&.content
257
+ new_content = note.content
258
+ .split(".\n").first.strip
259
+ .split(". ").first.strip
260
+ note.content = new_content.end_with?(".") ? new_content : "#{new_content}."
261
+ end
262
+ end
263
+ end
264
+
265
+ def should_preserve_complete_structure?(content)
266
+ return false if content.nil? || content.empty?
267
+
268
+ lines = content.split("\n")
269
+ first_paragraph = lines.first&.strip
270
+
271
+ if first_paragraph&.end_with?(":") && lines.length > 1
272
+ if first_paragraph.count(".").positive?
273
+ return false
274
+ end
275
+
276
+ remaining_content = lines[1..].join("\n")
277
+ return starts_with_list?(remaining_content.strip)
278
+ end
279
+
280
+ false
281
+ end
282
+
283
+ def compare_with_definitions(notes, definitions)
284
+ if notes&.first&.content == definitions&.first&.content
285
+ return []
286
+ end
287
+
288
+ notes
289
+ end
290
+
291
+ def remove_invalid_references(notes)
292
+ notes.reject do |note|
293
+ note.content.include?("image::") ||
294
+ note.content.match?(/<<(.*?){1,999}>>/)
295
+ end
296
+ end
297
+
298
+ def remove_redundant_note(notes)
299
+ notes.reject do |note|
300
+ note.content.match?(REDUNDANT_NOTE_REGEX) &&
301
+ !note.content.include?("\n")
302
+ end
303
+ end
304
+
305
+ def remove_see_content(notes)
306
+ notes.each do |note|
307
+ note.content = note.content.gsub(/\s+\(see(.*?){1,999}\)/, "")
308
+ end
309
+ end
310
+
311
+ def extract_file_type(filename)
312
+ match = filename.match(/(arm|mim|bom)_annotated\.exp$/)
313
+ return "resource" unless match
314
+
315
+ {
316
+ "arm" => "module_arm",
317
+ "mim" => "module_mim",
318
+ "bom" => "business_object_model",
319
+ }[match.captures[0]] || "resource"
320
+ end
321
+
322
+ def starts_with_list?(content)
323
+ return false if content.nil? || content.empty?
324
+
325
+ content.match?(/^\s*[*\-+]\s+/) || content.match?(/^\s*\d+\.\s+/)
326
+ end
327
+
328
+ def trim_definition(definition)
329
+ return nil if definition.nil? || definition.empty?
330
+
331
+ definition_str = definition.is_a?(Array) ? definition.join("\n\n") : definition.to_s
332
+
333
+ return nil if definition_str.empty?
334
+
335
+ paragraphs = definition_str.split("\n\n")
336
+ first_paragraph = paragraphs.first
337
+
338
+ combined = if paragraphs.length == 1
339
+ apply_first_sentence_logic(first_paragraph)
340
+ elsif first_paragraph.end_with?(":") && paragraphs.length > 1 && starts_with_list?(paragraphs[1])
341
+ complete_list = extract_complete_list(paragraphs, 1)
342
+ "#{first_paragraph}\n\n#{complete_list}"
343
+ else
344
+ apply_first_sentence_logic(first_paragraph)
345
+ end
346
+
347
+ combined = "#{combined}\n"
348
+ combined.gsub!(/\n\/\/.*?\n/, "\n")
349
+ combined.strip!
350
+
351
+ express_reference_to_mention(combined)
352
+ end
353
+
354
+ def apply_first_sentence_logic(paragraph)
355
+ new_content = paragraph
356
+ .split(".\n").first.strip
357
+ .split(". ").first.strip
358
+
359
+ new_content.end_with?(".") ? new_content : "#{new_content}."
360
+ end
361
+
362
+ def extract_complete_list(paragraphs, start_index)
363
+ return paragraphs[start_index] if start_index >= paragraphs.length
364
+
365
+ combined = paragraphs[start_index].dup
366
+ current_index = start_index + 1
367
+ in_continuation_block = combined.include?("--") && !combined.match?(/--.*--/m)
368
+
369
+ while current_index < paragraphs.length
370
+ next_para = paragraphs[current_index]
371
+
372
+ if next_para.match?(/^--\s*$/) || next_para.end_with?("--")
373
+ in_continuation_block = !in_continuation_block
374
+ combined += "\n\n#{next_para}"
375
+ current_index += 1
376
+ next
377
+ end
378
+
379
+ if in_continuation_block
380
+ combined += "\n\n#{next_para}"
381
+ current_index += 1
382
+ next
383
+ end
384
+
385
+ if starts_with_list?(next_para) || is_list_continuation?(next_para)
386
+ combined += "\n\n#{next_para}"
387
+ current_index += 1
388
+ in_continuation_block = true if next_para.include?("--") && !next_para.match?(/--.*--/m)
389
+ else
390
+ break
391
+ end
392
+ end
393
+
394
+ combined
395
+ end
396
+
397
+ def is_list_continuation?(content)
398
+ return false if content.nil? || content.empty?
399
+
400
+ content.match?(/^\+\s*$/) ||
401
+ content.match?(/^--\s*$/) ||
402
+ content.match?(/^\s{2,}/) ||
403
+ content.start_with?("which", "where", "that")
404
+ end
405
+
406
+ def express_reference_to_mention(description)
407
+ description
408
+ .gsub(/<<express:([\w.]+)>>/) do |_match|
409
+ full_ref = Regexp.last_match[1]
410
+ entity_id = full_ref.split(".").last
411
+ urn_mention(express_entity_urn(full_ref), entity_id)
412
+ end.gsub(/<<express:([\w.]+),([\w. ][\w. ]*)>>/) do |_match|
413
+ full_ref = Regexp.last_match[1]
414
+ display = Regexp.last_match(2)
415
+ urn_mention(express_entity_urn(full_ref), display)
416
+ end
417
+ end
418
+
419
+ def entity_name_to_text(entity_id)
420
+ entity_id.downcase.gsub("_", " ")
421
+ end
422
+
423
+ def generate_entity_definition(entity, schema, schema_type)
424
+ return "" if entity.nil?
425
+
426
+ entity_type = case schema_type
427
+ when "module_arm"
428
+ urn_mention(term_urn("general.application_object"),
429
+ "application object")
430
+ when "module_mim"
431
+ urn_mention(term_urn("express-language.entity_data_type"),
432
+ "entity data type")
433
+ when "resource", "business_object_model"
434
+ urn_mention(term_urn("express-language.entity_data_type"),
435
+ "entity data type")
436
+ else
437
+ raise Error, "[suma] encountered unsupported schema_type"
438
+ end
439
+
440
+ entity_ref = urn_mention(term_urn("express-language.entity"), "entity")
441
+
442
+ if entity.subtype_of.empty?
443
+ "#{entity_type} " \
444
+ "that represents the " \
445
+ "#{entity_name_to_text(entity.id)} #{entity_ref}"
446
+ else
447
+ entity_subtypes = entity.subtype_of.map do |e|
448
+ urn_mention(express_entity_urn("#{schema.id}.#{e.id}"), e.id)
449
+ end
450
+
451
+ "#{entity_type} that is a type of " \
452
+ "#{entity_subtypes.join(' and ')} " \
453
+ "that represents the " \
454
+ "#{entity_name_to_text(entity.id)} #{entity_ref}"
455
+ end
456
+ end
457
+
458
+ def convert_express_xref(content, _schema_domain)
459
+ content.gsub(/<<express:([\w.]+),([\w. ][\w. ]*)>>/) do
460
+ full_ref = Regexp.last_match(1)
461
+ display = Regexp.last_match(2)
462
+ urn_mention(express_entity_urn(full_ref), display)
463
+ end
464
+ end
465
+ end
466
+ end
data/lib/suma/urn.rb ADDED
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Suma
4
+ # Value object encapsulating URN semantics for ISO 10303 datasets.
5
+ #
6
+ # Normalises a URN prefix (stripping any trailing wildcard `:*`) and
7
+ # provides factory methods for composing leaf URNs:
8
+ #
9
+ # - `#for_schema(id)` → `<base>:tech:<id>`
10
+ # - `#for_term(id)` → `<base>:term:<id>`
11
+ # - `#for_entity(ref)` → `<base>:tech:<ref>`
12
+ #
13
+ # The wildcard form is preserved via `#wildcard` and `#aliases` so callers
14
+ # can populate `urnAliases` in register.yaml without re-implementing the
15
+ # normalisation logic.
16
+ class Urn
17
+ WILDCARD_SUFFIX = ":*"
18
+ TECH_COMPONENT = "tech"
19
+ TERM_COMPONENT = "term"
20
+
21
+ attr_reader :base
22
+
23
+ def initialize(raw)
24
+ @base = strip_wildcard(raw.to_s)
25
+ end
26
+
27
+ def to_s
28
+ base
29
+ end
30
+
31
+ def wildcard
32
+ "#{base}#{WILDCARD_SUFFIX}"
33
+ end
34
+
35
+ def aliases
36
+ [wildcard]
37
+ end
38
+
39
+ def for_schema(schema_id)
40
+ compose(TECH_COMPONENT, schema_id)
41
+ end
42
+
43
+ def for_term(concept_identifier)
44
+ compose(TERM_COMPONENT, concept_identifier)
45
+ end
46
+
47
+ def for_entity(full_ref)
48
+ compose(TECH_COMPONENT, full_ref)
49
+ end
50
+
51
+ private
52
+
53
+ def strip_wildcard(value)
54
+ value.sub(/#{Regexp.escape(WILDCARD_SUFFIX)}\z/o, "")
55
+ end
56
+
57
+ def compose(component, identifier)
58
+ "#{base}:#{component}:#{identifier}"
59
+ end
60
+ end
61
+ end
data/lib/suma/utils.rb CHANGED
@@ -3,8 +3,16 @@
3
3
  module Suma
4
4
  module Utils
5
5
  class << self
6
- def log(message)
7
- puts "[suma] #{message}"
6
+ attr_writer :output
7
+
8
+ def output
9
+ @output ||= $stderr
10
+ end
11
+
12
+ def log(message, level: :info)
13
+ return if level == :debug && !ENV["SUMA_DEBUG"]
14
+
15
+ output.puts "[suma] #{message}"
8
16
  end
9
17
  end
10
18
  end
data/lib/suma/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Suma
4
- VERSION = "0.2.5"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/suma.rb CHANGED
@@ -3,11 +3,40 @@
3
3
  require "expressir"
4
4
  require "lutaml/model"
5
5
 
6
- require_relative "suma/version"
7
- require_relative "suma/processor"
8
-
9
6
  module Suma
10
- class Error < StandardError; end
7
+ autoload :VERSION, "suma/version"
8
+
9
+ autoload :Processor, "suma/processor"
10
+ autoload :CollectionConfig, "suma/collection_config"
11
+ autoload :CollectionManifest, "suma/collection_manifest"
12
+ autoload :EengineConverter, "suma/eengine_converter"
13
+ autoload :ExpressSchema, "suma/express_schema"
14
+ autoload :LinkValidator, "suma/link_validator"
15
+ autoload :ManifestTraverser, "suma/manifest_traverser"
16
+ autoload :RegisterManifestGenerator, "suma/register_manifest_generator"
17
+ autoload :SchemaCategory, "suma/schema_category"
18
+ autoload :SchemaCollection, "suma/schema_collection"
19
+ autoload :SchemaComparer, "suma/schema_comparer"
20
+ autoload :SchemaCompiler, "suma/schema_compiler"
21
+ autoload :SchemaDiscovery, "suma/schema_discovery"
22
+ autoload :SchemaExporter, "suma/schema_exporter"
23
+ autoload :SchemaIndex, "suma/schema_index"
24
+ autoload :SchemaManifestGenerator, "suma/schema_manifest_generator"
25
+ autoload :SchemaNaming, "suma/schema_naming"
26
+ autoload :SchemaTemplate, "suma/schema_template"
27
+ autoload :SiteConfig, "suma/site_config"
28
+ autoload :SvgQuality, "suma/svg_quality"
29
+ autoload :TermExtractor, "suma/term_extractor"
30
+ autoload :ThorExt, "suma/thor_ext"
31
+ autoload :Urn, "suma/urn"
32
+ autoload :Utils, "suma/utils"
11
33
 
12
- # Your code goes here...
34
+ autoload :Cli, "suma/cli"
35
+ autoload :Eengine, "suma/eengine"
36
+ autoload :Jsdai, "suma/jsdai"
37
+
38
+ class Error < StandardError; end
39
+ class SchemaNotFoundError < Error; end
40
+ class CompilationError < Error; end
41
+ class EengineNotAvailableError < Error; end
13
42
  end
data/suma.gemspec CHANGED
@@ -34,12 +34,13 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
34
34
  spec.require_paths = ["lib"]
35
35
 
36
36
  spec.add_dependency "expressir", ">= 2.1.29", "~> 2.1"
37
- spec.add_dependency "glossarist", "~> 2.4.0"
38
- spec.add_dependency "lutaml-model", "~> 0.7"
37
+ spec.add_dependency "glossarist", "~> 2.8", ">= 2.8.7"
38
+ spec.add_dependency "lutaml-model", "~> 0.8.0"
39
39
  spec.add_dependency "metanorma", "~> 2.3"
40
40
  spec.add_dependency "plurimath"
41
41
  spec.add_dependency "ruby-progressbar"
42
42
  spec.add_dependency "rubyzip", "~> 2.3"
43
+ spec.add_dependency "svg_conform", "~> 0.1.0"
43
44
  spec.add_dependency "table_tennis"
44
45
  spec.add_dependency "thor", ">= 0.20"
45
46