suma 0.1.14 → 0.1.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d52aaad6ea81bb43e193cdf0c8e190dfe02f71791e218314a401ae3bea254cd3
4
- data.tar.gz: a573936a7eabfad21a4768e6c32869ecc286a2980feeaf8350720b674c604f34
3
+ metadata.gz: 955161f5bfcd18f46e1fcafafdc05bea5a7310eaa8899b03552e7d22f6f41795
4
+ data.tar.gz: ff06abeaf766ce76d814079ef718c6deb45e1dd0cae5d89e9d03d203e3a5635d
5
5
  SHA512:
6
- metadata.gz: d7278d60481858a6507bf97f738df21f0168c60c6453f9de4479545c589450f63c148923be20b31a8bb88e50344adbf8521cfe12a826dbff3103e492782727f3
7
- data.tar.gz: b37f9d1f0676607011f6f2059f0b639a563273930eb7961ee52866c04b30df14a7b88076752f4bf2ff4cfd4911a402b547b1c486c42cd9cc381d4e631bc6aac4
6
+ metadata.gz: 2568587bc47fe2eb892fc7e9f8f4981aceaa06eef4b27ad54cb427e9082cc23571a2c26f8ae9380dfcd52fbb40f38a77ad0d2282e7ada54d8744e3e21edcd301
7
+ data.tar.gz: 4b8505d33b3eef150aeb76f23548f5983740d1940488a0ee1c7d6e515cd249e5fa74fbdadb72543a91104ca9ce609eb0f374dd01c033db8eee2bb7585525b09f
data/.gitignore CHANGED
@@ -9,3 +9,7 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ .rubocop_todo.yml
13
+ .rubocop-https---*-yml
14
+ .ruby-version
15
+ Gemfile.lock
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-04-03 06:48:36 UTC using RuboCop version 1.75.1.
3
+ # on 2025-07-05 22:42:28 UTC using RuboCop version 1.77.0.
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
@@ -13,43 +13,32 @@ Gemspec/DuplicatedAssignment:
13
13
  Exclude:
14
14
  - 'suma.gemspec'
15
15
 
16
- # Offense count: 2
17
- # This cop supports safe autocorrection (--autocorrect).
18
- # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
19
- # SupportedHashRocketStyles: key, separator, table
20
- # SupportedColonStyles: key, separator, table
21
- # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
22
- Layout/HashAlignment:
23
- Exclude:
24
- - 'lib/suma/cli/validate.rb'
25
-
26
- # Offense count: 55
16
+ # Offense count: 57
27
17
  # This cop supports safe autocorrection (--autocorrect).
28
- # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
18
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
29
19
  # URISchemes: http, https
30
20
  Layout/LineLength:
31
21
  Exclude:
32
22
  - 'lib/suma/cli.rb'
33
23
  - 'lib/suma/cli/build.rb'
24
+ - 'lib/suma/cli/extract_terms.rb'
34
25
  - 'lib/suma/cli/validate.rb'
35
26
  - 'lib/suma/cli/validate_ascii.rb'
36
27
  - 'lib/suma/cli/validate_links.rb'
37
- - 'lib/suma/collection_manifest.rb'
38
28
  - 'lib/suma/processor.rb'
39
29
  - 'lib/suma/schema_attachment.rb'
40
30
  - 'lib/suma/schema_collection.rb'
41
31
  - 'lib/suma/schema_document.rb'
42
32
  - 'lib/suma/thor_ext.rb'
33
+ - 'spec/suma/cli/extract_terms_spec.rb'
43
34
  - 'spec/suma/cli/validate_ascii_spec.rb'
44
35
  - 'suma.gemspec'
45
36
 
46
- # Offense count: 3
47
- # This cop supports safe autocorrection (--autocorrect).
48
- # Configuration parameters: AllowInHeredoc.
49
- Layout/TrailingWhitespace:
37
+ # Offense count: 1
38
+ # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
39
+ Lint/DuplicateBranch:
50
40
  Exclude:
51
- - 'lib/suma/cli.rb'
52
- - 'lib/suma/cli/validate.rb'
41
+ - 'lib/suma/cli/extract_terms.rb'
53
42
 
54
43
  # Offense count: 2
55
44
  Lint/DuplicateMethods:
@@ -57,10 +46,11 @@ Lint/DuplicateMethods:
57
46
  - 'lib/suma/cli/validate_ascii.rb'
58
47
  - 'lib/suma/express_schema.rb'
59
48
 
60
- # Offense count: 15
49
+ # Offense count: 20
61
50
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
62
51
  Metrics/AbcSize:
63
52
  Exclude:
53
+ - 'lib/suma/cli/extract_terms.rb'
64
54
  - 'lib/suma/cli/validate_ascii.rb'
65
55
  - 'lib/suma/cli/validate_links.rb'
66
56
  - 'lib/suma/schema_attachment.rb'
@@ -73,15 +63,16 @@ Metrics/AbcSize:
73
63
  Metrics/BlockLength:
74
64
  Max: 64
75
65
 
76
- # Offense count: 4
66
+ # Offense count: 8
77
67
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
78
68
  Metrics/CyclomaticComplexity:
79
69
  Exclude:
70
+ - 'lib/suma/cli/extract_terms.rb'
80
71
  - 'lib/suma/cli/validate_ascii.rb'
81
72
  - 'lib/suma/cli/validate_links.rb'
82
73
  - 'lib/suma/thor_ext.rb'
83
74
 
84
- # Offense count: 21
75
+ # Offense count: 30
85
76
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
86
77
  Metrics/MethodLength:
87
78
  Max: 107
@@ -91,10 +82,11 @@ Metrics/MethodLength:
91
82
  Metrics/ParameterLists:
92
83
  Max: 6
93
84
 
94
- # Offense count: 2
85
+ # Offense count: 5
95
86
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
96
87
  Metrics/PerceivedComplexity:
97
88
  Exclude:
89
+ - 'lib/suma/cli/extract_terms.rb'
98
90
  - 'lib/suma/cli/validate_ascii.rb'
99
91
  - 'lib/suma/cli/validate_links.rb'
100
92
 
@@ -113,12 +105,12 @@ Performance/CollectionLiteralInLoop:
113
105
  Exclude:
114
106
  - 'spec/suma/cli_spec.rb'
115
107
 
116
- # Offense count: 3
108
+ # Offense count: 9
117
109
  # Configuration parameters: CountAsOne.
118
110
  RSpec/ExampleLength:
119
- Max: 16
111
+ Max: 44
120
112
 
121
- # Offense count: 5
113
+ # Offense count: 6
122
114
  RSpec/MultipleExpectations:
123
115
  Max: 12
124
116
 
@@ -136,3 +128,9 @@ Style/EmptyElse:
136
128
  # SupportedStyles: annotated, template, unannotated
137
129
  Style/FormatStringToken:
138
130
  EnforcedStyle: unannotated
131
+
132
+ # Offense count: 1
133
+ # Configuration parameters: Max.
134
+ Style/SafeNavigationChainLength:
135
+ Exclude:
136
+ - 'lib/suma/cli/extract_terms.rb'
data/README.adoc CHANGED
@@ -331,8 +331,10 @@ out which schemas the document includes.
331
331
 
332
332
  === Extract terms command
333
333
 
334
- The `suma extract_terms` command extracts terms from SCHEMA_MANIFEST_FILE and
335
- generates Glossarist v2 dataset in the output directory.
334
+ The `suma extract_terms` command extracts terms from EXPRESS schemas and
335
+ generates a Glossarist v2 dataset in the output directory. This command processes
336
+ various types of STEP schemas and creates standardized terminology datasets
337
+ suitable for glossary and dictionary applications.
336
338
 
337
339
  [source,sh]
338
340
  ----
@@ -341,38 +343,80 @@ $ suma extract_terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH [options]
341
343
 
342
344
  Parameters:
343
345
 
344
- `SCHEMA_MANIFEST_FILE`:: Path to SCHEMA_MANIFEST_FILE
346
+ `SCHEMA_MANIFEST_FILE`:: Path to the schema manifest file that lists all schemas
347
+ to process (e.g., "schemas-smrl-all.yml")
345
348
 
346
- `GLOSSARIST_OUTPUT_PATH`:: Path to the output directory for the Glossarist v2
347
- dataset
349
+ `GLOSSARIST_OUTPUT_PATH`:: Path to the output directory where the Glossarist v2
350
+ dataset will be generated
348
351
 
349
352
  Options:
350
353
 
351
- `language_code`:: Language code for the Glossarist (default: "eng")
354
+ `--language_code`, `-l`:: Language code for the Glossarist dataset (default: "eng")
352
355
 
356
+ ==== Supported schema types
357
+
358
+ The command supports extraction from the following EXPRESS schema types:
359
+
360
+ * **ARM (Application Reference Model)** - Application module schemas ending with `_arm`
361
+ * **MIM (Module Implementation Model)** - Application module schemas ending with `_mim`
362
+ * **Resource schemas** - General resource schemas
363
+ * **BOM (Business Object Model)** - Business object model schemas ending with `_bom`
364
+
365
+ ==== Extracted terms
366
+
367
+ The command extracts the following types of terms from EXPRESS schemas:
368
+
369
+ * **Entities** - EXPRESS entity definitions with their attributes and relationships
370
+ * **Types** - EXPRESS type definitions including enumerations and select types
371
+ * **Functions** - EXPRESS function definitions
372
+ * **Procedures** - EXPRESS procedure definitions
373
+ * **Constants** - EXPRESS constant definitions
374
+
375
+ Each extracted term includes:
376
+
377
+ * Unique identifier based on schema and term name
378
+ * Term definition and description
379
+ * Source schema information
380
+ * Appropriate domain classification (application module, resource, or business object model)
381
+
382
+ ==== Output format
383
+
384
+ The command generates a Glossarist v2 compliant dataset with:
385
+
386
+ * `concept/` directory containing concept definition files
387
+ * `localized_concept/` directory containing localized concept files
388
+ * YAML format following Glossarist v2 schema specifications
389
+ * Proper cross-references and citations to source schemas
390
+
391
+ .To extract terms from a schema manifest file
353
392
  [example]
354
393
  ====
355
- .To extract terms from SCHEMA_MANIFEST_FILE
356
394
  [source,sh]
357
395
  ----
358
- $ bundle exec suma extract_terms path/to/schemas-smrl-all.yml glossarist_output
359
- # => generates glossarist_output/concept/foo.yaml and glossarist_output/localized_concept/bar.yaml
396
+ $ bundle exec suma extract_terms schemas-smrl-all.yml glossarist_output
397
+ # => generates glossarist_output/concept/*.yaml and glossarist_output/localized_concept/*.yaml
360
398
  ----
399
+ ====
361
400
 
362
- .To extract terms from SCHEMA_MANIFEST_FILE with language code "fra"
401
+ .To extract terms from a specific schema subset
402
+ [example]
403
+ ====
363
404
  [source,sh]
364
405
  ----
365
- $ bundle exec suma extract_terms path/to/schemas-smrl-all.yml glossarist_output -l fra
366
- # => generates glossarist_output/concept/foo.yaml and glossarist_output/localized_concept/bar.yaml
406
+ $ bundle exec suma extract_terms schemas-activity-modules.yml terms_output
407
+ # => processes only schemas listed in the manifest file
367
408
  ----
368
409
  ====
369
410
 
411
+ The generated dataset is meant to be used for ISO 10303-2.
412
+
370
413
 
371
414
  == Usage: Ruby
372
415
 
373
416
  === General
374
417
 
375
- Suma can be used programmatically in your Ruby applications. The following examples demonstrate common usage patterns.
418
+ Suma can be used programmatically in your Ruby applications. The following
419
+ examples demonstrate common usage patterns.
376
420
 
377
421
  === Building collections
378
422
 
@@ -4,7 +4,6 @@ require "thor"
4
4
  require_relative "../thor_ext"
5
5
  require "fileutils"
6
6
  require "expressir"
7
- require "yaml"
8
7
  require "securerandom"
9
8
  require "glossarist"
10
9
 
@@ -13,29 +12,30 @@ module Suma
13
12
  # ExtractTerms command using Expressir to extract terms into the
14
13
  # Glossarist v2 format
15
14
  class ExtractTerms < Thor
15
+ # Matches patterns like "A thing is a type of {{entity}}." or
16
+ # "An object is a type of a {{entity}}"
17
+ REDUNDANT_NOTE_REGEX =
18
+ %r{
19
+ ^An? # Starts with "A" or "An"
20
+ \s.*?\sis\sa\stype\sof # Text followed by "is a type of"
21
+ (\sa|\san)? # Optional " a" or " an"
22
+ \s\{\{[^\}]*\}\} # Text in double curly braces
23
+ \s*?\.?$ # Optional whitespace and period at the end
24
+ }x
25
+
16
26
  desc "extract_terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH",
17
27
  "Extract terms from SCHEMA_MANIFEST_FILE into " \
18
28
  "Glossarist v2 format"
19
29
  option :language_code, type: :string, default: "eng", aliases: "-l",
20
30
  desc: "Language code for the Glossarist"
21
31
 
22
- YAML_FILE_EXTENSIONS = [".yaml", ".yml"].freeze
23
-
24
- def extract_terms(schema_manifest_file, output_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
32
+ def extract_terms(schema_manifest_file, output_path)
25
33
  language_code = options[:language_code]
26
34
  schema_manifest_file = File.expand_path(schema_manifest_file)
27
35
 
28
- if File.file?(schema_manifest_file)
29
- unless File.exist?(schema_manifest_file)
30
- raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
31
- "`#{schema_manifest_file}` not found."
32
- end
33
-
34
- if !YAML_FILE_EXTENSIONS.include?(File.extname(schema_manifest_file))
35
- raise ArgumentError, "Specified SCHEMA_MANIFEST_FILE " \
36
- "`#{schema_manifest_file}` " \
37
- "is not a YAML file."
38
- end
36
+ unless File.exist?(schema_manifest_file)
37
+ raise Errno::ENOENT, "Specified SCHEMA_MANIFEST_FILE " \
38
+ "`#{schema_manifest_file}` not found."
39
39
  end
40
40
 
41
41
  run(schema_manifest_file, output_path, language_code)
@@ -44,152 +44,185 @@ module Suma
44
44
  private
45
45
 
46
46
  def run(schema_manifest_file, output_path, language_code = "eng")
47
- exp_files = get_exp_files(schema_manifest_file)
48
-
49
- exp_files.map do |exp_file|
47
+ get_exp_files(schema_manifest_file).map do |exp_file|
50
48
  extract(exp_file, output_path, language_code)
51
49
  end
52
50
  end
53
51
 
54
52
  def get_exp_files(schema_manifest_file)
55
- data = YAML.safe_load(
56
- File.read(schema_manifest_file, encoding: "UTF-8"),
57
- permitted_classes: [Date, Time, Symbol],
58
- permitted_symbols: [],
59
- aliases: true,
60
- )
61
-
62
- paths = data["schemas"].values.filter_map { |v| v["path"] }
53
+ config = Expressir::SchemaManifest.from_file(schema_manifest_file)
54
+ paths = config.schemas.map(&:path)
63
55
 
64
56
  if paths.empty?
65
57
  raise Errno::ENOENT, "No EXPRESS files found in " \
66
58
  "`#{schema_manifest_file}`."
67
59
  end
68
60
 
69
- # resolve paths relative to the directory of the schema manifest file
70
- paths.map do |path|
71
- File.expand_path(path, File.dirname(schema_manifest_file))
72
- end
61
+ paths
73
62
  end
74
63
 
75
- def extract(exp_file, output_path, language_code) # rubocop:disable Metrics/AbcSize
76
- puts "Processing EXPRESS file: #{exp_file}"
64
+ def extract(exp_file, output_path, language_code)
65
+ exp_path_rel = Pathname.new(exp_file).relative_path_from(Pathname.getwd)
66
+ puts "Building terms: #{exp_path_rel}"
67
+
77
68
  repo = Expressir::Express::Parser.from_file(exp_file)
78
69
  schema = get_default_schema(repo)
79
70
 
71
+ unless schema.file
72
+ raise Error.new("Schema must have an associated file")
73
+ end
74
+
80
75
  collection = build_managed_concept_collection(
81
76
  schema, language_code
82
77
  )
83
78
 
84
- output_data(collection, output_path, exp_file)
79
+ output_data(collection, output_path)
85
80
  end
86
81
 
87
- def output_data(collection, output_path, exp_file)
82
+ def output_data(collection, output_path)
88
83
  unless File.exist?(output_path)
89
84
  FileUtils.mkdir_p(File.expand_path(output_path))
90
85
  end
91
86
 
92
- puts "Saving collection to files in: #{File.expand_path(output_path)}"
87
+ puts "Saving collection to files in: #{output_path}"
93
88
  collection.save_to_files(File.expand_path(output_path))
94
89
 
95
- puts "Processing EXPRESS file: #{exp_file}...Done."
96
90
  collection
97
91
  end
98
92
 
99
- def build_managed_concept_collection(schema, language_code) # rubocop:disable Metrics/AbcSize
100
- managed_concept_data = Glossarist::ManagedConceptData.new
101
- managed_concept_data.id = get_identifier(schema)
102
-
103
- localized_concept_id = SecureRandom.uuid
104
- localized_concept = build_localized_concept(
105
- schema, language_code, localized_concept_id
106
- )
107
-
108
- managed_concept_data
109
- .localizations[localized_concept.language_code] = localized_concept
110
-
111
- managed_concept_data.localized_concepts = {
112
- localized_concept.language_code => localized_concept_id,
113
- }
114
-
115
- managed_concept = Glossarist::ManagedConcept.new
116
- managed_concept.uuid = SecureRandom.uuid
117
- managed_concept.data = managed_concept_data
118
-
119
- collection = Glossarist::ManagedConceptCollection.new
120
- collection.store(managed_concept)
121
- collection
93
+ def build_managed_concept_collection(schema, language_code)
94
+ Glossarist::ManagedConceptCollection.new.tap do |collection|
95
+ # Extract schema-level citation data once to reuse across all entities
96
+ source_ref = get_source_ref(schema)
97
+
98
+ # Create one concept per entity
99
+ schema.entities.each do |entity|
100
+ localized_concept = build_localized_concept(
101
+ schema: schema,
102
+ entity: entity,
103
+ language_code: language_code,
104
+ source_ref: source_ref,
105
+ )
106
+
107
+ managed_data = Glossarist::ManagedConceptData.new.tap do |data|
108
+ data.id = get_entity_identifier(schema, entity)
109
+
110
+ # TODO: Why do we need both localizations and localized_concepts??
111
+ data.localizations[language_code] = localized_concept
112
+ # uuid is automatically set from the serialization of the object
113
+ data.localized_concepts = {
114
+ language_code => get_localized_concept_identifier(
115
+ schema, entity, language_code
116
+ ),
117
+ }
118
+ end
119
+
120
+ managed_concept = Glossarist::ManagedConcept.new.tap do |concept|
121
+ # uuid is automatically set from the serialization of the object
122
+ concept.id = get_entity_identifier(schema, entity)
123
+ concept.uuid = concept.id
124
+ concept.data = managed_data
125
+ end
126
+
127
+ collection.store(managed_concept)
128
+ end
129
+ end
122
130
  end
123
131
 
124
- def build_localized_concept(schema, language_code, localized_concept_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
132
+ def build_localized_concept(schema:, entity:, language_code:, source_ref:)
125
133
  schema_domain = get_domain(schema)
126
134
 
127
- localized_concept_data = Glossarist::ConceptData.new
128
- localized_concept_data.terms = get_terms(schema) || []
129
- localized_concept_data.definition = get_definitions(schema) || []
130
- localized_concept_data.notes = get_notes(schema, schema_domain) || []
131
- localized_concept_data.examples = get_examples(schema,
132
- schema_domain) || []
133
- localized_concept_data.language_code = language_code
134
- localized_concept_data.domain = schema_domain
135
- localized_concept_data.sources = get_source_ref(schema) || []
135
+ localized_concept_data = Glossarist::ConceptData.new.tap do |data|
136
+ data.terms = get_entity_terms(entity)
137
+ data.definition = get_entity_definitions(entity, schema)
138
+ data.language_code = language_code
139
+ data.domain = schema_domain
140
+ data.sources = [source_ref] if source_ref
136
141
 
137
- localized_concept = Glossarist::LocalizedConcept.new
138
- localized_concept.data = localized_concept_data
142
+ # Only assign optional fields if they have content
143
+ notes = get_entity_notes(entity, schema_domain, data.definition)
144
+ data.notes = notes if notes && !notes.empty?
145
+
146
+ # examples = get_entity_examples(entity, schema_domain)
147
+ # data.examples = examples if examples && !examples.empty?
148
+ data.examples = []
149
+ end
139
150
 
140
- localized_concept.uuid = localized_concept_id
141
- localized_concept
151
+ Glossarist::LocalizedConcept.new.tap do |concept|
152
+ concept.data = localized_concept_data
153
+ end
142
154
  end
143
155
 
156
+ # We only deal with 1 schema
144
157
  def get_default_schema(repo)
145
158
  repo.schemas.first
146
159
  end
147
160
 
148
- def get_identifier(schema)
149
- remark_item = schema.remark_items.find do |s|
150
- s.id == "__identifier"
151
- end
152
- remark_item.remarks.first || SecureRandom.uuid
161
+ def find_remark_value(schema, remark_id)
162
+ schema.remark_items.find { |s| s.id == remark_id }&.remarks&.first
153
163
  end
154
164
 
155
- def get_title(schema)
156
- remark_item = schema.remark_items.find do |s|
157
- s.id == "__title"
158
- end
159
- remark_item.remarks.first
165
+ def get_entity_identifier(schema, entity)
166
+ "#{schema.id}.#{entity.id}"
167
+ end
168
+
169
+ def get_localized_concept_identifier(schema, entity, lang)
170
+ "#{schema.id}.#{entity.id}-#{lang}"
160
171
  end
161
172
 
162
173
  def get_source_ref(schema)
163
- remark_item = schema.remark_items.find do |s|
164
- s.id == "__published_in"
174
+ origin = Glossarist::Citation.new.tap do |citation|
175
+ citation.ref = "ISO 10303"
176
+ custom_locality = build_custom_locality(schema)
177
+
178
+ unless custom_locality.empty?
179
+ citation.custom_locality = custom_locality
180
+ end
165
181
  end
166
- ref = remark_item&.remarks&.first
167
182
 
168
- if ref
169
- Glossarist::ConceptSource.new(
170
- type: "authoritative",
171
- origin: Glossarist::Citation.new(ref: ref),
183
+ Glossarist::ConceptSource.new(type: "authoritative", origin: origin)
184
+ end
185
+
186
+ # SCHEMA action_schema
187
+ # '{iso standard 10303 part(41) version(9) object(1) action-schema(1)}';
188
+ def build_custom_locality(schema)
189
+ [].tap do |localities|
190
+ # Add schema name
191
+ localities << Glossarist::CustomLocality.new(
192
+ name: "schema",
193
+ value: schema.id,
172
194
  )
195
+
196
+ # Add version if available
197
+ version_item = schema.version.items.detect { |i| i.name == "version" }
198
+ if version_item
199
+ localities << Glossarist::CustomLocality.new(
200
+ name: "version",
201
+ value: version_item.value,
202
+ )
203
+ end
173
204
  end
174
205
  end
175
206
 
207
+ # TODO: What if this was a "bom"?
176
208
  def get_domain(schema)
177
- prefix = module?(schema) ? "application module" : "resource"
178
- "#{prefix}: #{schema.id}"
179
- end
180
-
181
- def module?(schema)
182
- remark_item = schema.remark_items.find do |s|
183
- s.id == "__schema_file"
184
- end
209
+ prefix = if mim?(schema.id) || arm?(schema.id)
210
+ "application module"
211
+ else
212
+ "resource"
213
+ end
185
214
 
186
- File.basename(remark_item.remarks.first, ".*") == "module"
215
+ "#{prefix}: #{schema.id}"
187
216
  end
188
217
 
189
218
  def arm?(schema_id)
190
219
  schema_id.end_with?("_arm")
191
220
  end
192
221
 
222
+ def mim?(schema_id)
223
+ schema_id.end_with?("_mim")
224
+ end
225
+
193
226
  def get_terms(schema)
194
227
  schema_title = get_title(schema)
195
228
  if schema_title
@@ -203,69 +236,291 @@ module Suma
203
236
  end
204
237
  end
205
238
 
206
- def get_schema_type(schema)
207
- return "resource" if !module?(schema)
239
+ def get_entity_terms(entity)
240
+ # For now, use the entity ID as the term
241
+ # This could be enhanced to look for entity-specific title remark items
242
+ [
243
+ Glossarist::Designation::Base.new(
244
+ designation: entity.id,
245
+ type: "expression",
246
+ normative_status: "preferred",
247
+ ),
248
+ ]
249
+ end
208
250
 
209
- return "arm" if arm?(schema.id)
251
+ def get_entity_definitions(entity, schema)
252
+ schema_type = extract_file_type(schema.file)
253
+ schema_domain = get_domain(schema)
210
254
 
211
- "min"
255
+ definition = generate_entity_definition(entity, schema_domain,
256
+ schema_type)
257
+ [Glossarist::DetailedDefinition.new(content: definition)]
212
258
  end
213
259
 
214
- def get_definitions(schema)
215
- type = get_schema_type(schema)
216
- subtype = get_subtype_of(schema)
260
+ def get_entity_notes(entity, schema_domain, definitions)
261
+ puts "Extracting notes for entity: #{entity.id}"
262
+ notes = []
263
+
264
+ notes = add_entity_notes(entity, schema_domain, notes)
265
+ # notes = add_other_notes(entity, schema_domain, notes)
266
+ notes = only_keep_first_sentence(notes)
267
+ notes = remove_see_content(notes)
268
+ notes = remove_redundant_note(notes)
269
+ notes = remove_invalid_references(notes)
270
+ compare_with_definitions(notes, definitions)
271
+ end
217
272
 
218
- represent_str = "that represents the #{get_title(schema)} {{entity}}"
219
- if subtype
220
- represent_str = "that is a type of #{subtype} #{represent_str}"
273
+ def add_entity_notes(entity, schema_domain, notes)
274
+ # Add trimmed definition from entity description as first note
275
+ if entity.remarks && !entity.remarks.empty?
276
+ trimmed_def = trim_definition(entity.remarks)
277
+ if trimmed_def && !trimmed_def.empty?
278
+ notes << Glossarist::DetailedDefinition.new(
279
+ content: convert_express_xref(trimmed_def, schema_domain),
280
+ )
281
+ end
221
282
  end
222
283
 
223
- definition = case type
224
- when "arm"
225
- "{{application object}} #{represent_str}"
226
- else
227
- "{{entity data type}} #{represent_str}"
228
- end
229
- [Glossarist::DetailedDefinition.new(content: definition)]
230
- end
231
-
232
- def get_subtype_of(schema)
233
- schema.entities.first&.subtype_of&.first&.id # rubocop:disable Style/SafeNavigationChainLength
284
+ notes.compact
234
285
  end
235
286
 
236
- # get entities remarks and remark items with id `__note` as notes
237
- def get_notes(schema, schema_domain) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize
238
- notes = schema.entities&.map do |entity|
239
- [
240
- entity.remarks,
241
- entity.remark_items&.select do |ri|
242
- ri.id == "__note"
243
- end&.map(&:remarks),
244
- ]
245
- end&.flatten&.compact
287
+ def add_other_notes(entity, schema_domain, notes)
288
+ # Add other notes from entity remarks
289
+ other_notes = [
290
+ entity.remark_items&.select do |ri|
291
+ ri.id == "__note"
292
+ end&.map(&:remarks),
293
+ ].flatten.compact
246
294
 
247
- notes&.map do |note|
248
- Glossarist::DetailedDefinition.new(
295
+ other_notes.each do |note|
296
+ notes << Glossarist::DetailedDefinition.new(
249
297
  content: convert_express_xref(note, schema_domain),
250
298
  )
251
299
  end
300
+
301
+ notes
252
302
  end
253
303
 
254
- # get entities remark items with id `__example` as examples
255
- def get_examples(schema, schema_domain) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/AbcSize
256
- examples = schema.entities&.map do |entity|
257
- entity.remark_items&.select do |ri|
258
- ri.id == "__example"
259
- end&.map(&:remarks)
260
- end&.flatten&.compact
304
+ # https://github.com/metanorma/iso-10303/issues/621
305
+ # 1. First sentence in first paragraph of the entity description
306
+ # (in EXPRESS remark) becomes NOTE 1 in ISO 10303-2 of the entity.
307
+ def only_keep_first_sentence(notes)
308
+ notes.each do |note|
309
+ # Split by period and take the first sentence
310
+ # Avoid splitting by pattern like "abc.def"
311
+ if note&.content
312
+ new_content = note.content
313
+ .split(".\n").first.strip
314
+ .split(". ").first.strip
315
+ note.content = if new_content.end_with?(".")
316
+ new_content
317
+ else
318
+ "#{new_content}."
319
+ end
320
+ end
321
+ end
322
+ end
323
+
324
+ # https://github.com/metanorma/iso-10303/issues/621
325
+ # 2. If this first sentence matches the 7-word magic sentence
326
+ # (2-3 forms of that), it is discarded so there will not be a NOTE 1.
327
+ def compare_with_definitions(notes, definitions)
328
+ if notes&.first&.content == definitions&.first&.content
329
+ # Discarding first note as it matches the definition
330
+ return []
331
+ end
332
+
333
+ notes
334
+ end
335
+
336
+ # https://github.com/metanorma/iso-10303/issues/621
337
+ # 3. No reference to any types or attribute or figures allowed in first
338
+ # sentence. Entity references “{{…}}” are allowed.
339
+ def remove_invalid_references(notes)
340
+ notes.reject do |note|
341
+ note.content.include?("image::") ||
342
+ note.content.match?(/<<(.*?){1,999}>>/)
343
+ end
344
+ end
345
+
346
+ # https://github.com/metanorma/iso-10303/issues/621
347
+ # 4. Entity notes and examples in EXPRESS remarks are NOT represented in
348
+ # part 2.
349
+ def remove_redundant_note(notes)
350
+ notes.reject do |note|
351
+ note.content.match?(REDUNDANT_NOTE_REGEX) &&
352
+ !note.content.include?("\n")
353
+ end
354
+ end
355
+
356
+ # https://github.com/metanorma/iso-10303/issues/621
357
+ # 5. If the sentence contains “\s+(see …)”, the contents including the
358
+ # parentheses are removed.
359
+ def remove_see_content(notes)
360
+ notes.each do |note|
361
+ note.content = note.content.gsub(/\s+\(see(.*?){1,999}\)/, "")
362
+ end
363
+ end
364
+
365
+ def get_entity_examples(entity, schema_domain)
366
+ examples = entity.remark_items&.select do |ri|
367
+ ri.id == "__example"
368
+ end&.map(&:remarks)&.flatten&.compact || []
261
369
 
262
- examples&.map do |example|
370
+ examples.map do |example|
263
371
  Glossarist::DetailedDefinition.new(
264
372
  content: convert_express_xref(example, schema_domain),
265
373
  )
266
374
  end
267
375
  end
268
376
 
377
+ def extract_file_type(filename)
378
+ match = filename.match(/(arm|mim|bom)_annotated\.exp$/)
379
+ return "resource" unless match
380
+
381
+ {
382
+ "arm" => "module_arm",
383
+ "mim" => "module_mim",
384
+ "bom" => "business_object_model",
385
+ }[match.captures[0]] || "resource"
386
+ end
387
+
388
+ def get_schema_type(schema)
389
+ return "mim" if mim?(schema.id)
390
+ return "arm" if arm?(schema.id)
391
+ return "bom" if bom?(schema.id)
392
+
393
+ "resource"
394
+ end
395
+
396
+ def bom?(schema_id)
397
+ schema_id.end_with?("_bom")
398
+ end
399
+
400
+ # rubocop:disable Metrics/MethodLength
401
+ def combine_paragraphs(full_paragraph, next_paragraph)
402
+ # If full_paragraph already contains a period, extract that.
403
+ if m = full_paragraph.match(/\A(?<inner_first>[^\n]*?\.)\s/)
404
+ # puts "CONDITION 1"
405
+ if m[:inner_first]
406
+ return m[:inner_first]
407
+ else
408
+ return full_paragraph
409
+ end
410
+ end
411
+
412
+ # If full_paragraph ends with a period, this is the last.
413
+ if /\.\s*\Z/.match?(full_paragraph)
414
+ # puts "CONDITION 2"
415
+ return full_paragraph
416
+ end
417
+
418
+ # If next_paragraph is a list
419
+ if next_paragraph.start_with?("*")
420
+ # puts "CONDITION 3"
421
+ return "#{full_paragraph}\n\n#{next_paragraph}"
422
+ end
423
+
424
+ # If next_paragraph is a continuation of a list
425
+ if next_paragraph.start_with?("which", "that")
426
+ # puts "CONDITION 4"
427
+ return "#{full_paragraph}\n\n#{next_paragraph}"
428
+ end
429
+
430
+ # puts "CONDITION 5"
431
+ full_paragraph
432
+ end
433
+
434
+ def trim_definition(definition)
435
+ return nil if definition.nil? || definition.empty?
436
+
437
+ # Handle case where definition is an array
438
+ definition_str = if definition.is_a?(Array)
439
+ definition.join("\n\n")
440
+ else
441
+ definition.to_s
442
+ end
443
+
444
+ return nil if definition_str.empty?
445
+
446
+ # Unless the first paragraph ends with "between" and is followed by a
447
+ # list, don't split
448
+ paragraphs = definition_str.split("\n\n")
449
+
450
+ # puts paragraphs.inspect
451
+
452
+ first_paragraph = paragraphs.first
453
+
454
+ combined = if paragraphs.length > 1
455
+ paragraphs[1..].inject(first_paragraph) do |acc, p|
456
+ combine_paragraphs(acc, p)
457
+ end
458
+ else
459
+ combine_paragraphs(first_paragraph, "")
460
+ end
461
+
462
+ # puts "combined--------- #{combined}"
463
+
464
+ # Remove comments until end of line
465
+ combined = "#{combined}\n"
466
+ combined.gsub!(/\n\/\/.*?\n/, "\n")
467
+ combined.strip!
468
+
469
+ express_reference_to_mention(combined)
470
+ end
471
+ # rubocop:enable Metrics/MethodLength
472
+
473
+ # Replace `<<express:{schema}.{entity}>>` with {{entity}}
474
+ # and `<<express:{schema}.{entity},{render}>>` with {{entity,render}}
475
+ def express_reference_to_mention(description)
476
+ # TODO: Use Expressir to check whether the "entity" is really an
477
+ # EXPRESS ENTITY. If not, skip the mention.
478
+ description
479
+ .gsub(/<<express:([^,]+)>>/) do |_match|
480
+ "{{#{Regexp.last_match[1].split('.').last}}}"
481
+ end.gsub(/<<express:([^,]+),([^>]+)>>/) do |_match|
482
+ "{{#{Regexp.last_match[1].split('.').last}," \
483
+ "#{Regexp.last_match[2]}}}"
484
+ end
485
+ end
486
+
487
+ def entity_name_to_text(entity_id)
488
+ entity_id.downcase.gsub("_", " ")
489
+ end
490
+
491
+ # rubocop:disable Layout/LineLength
492
+ def generate_entity_definition(entity, _domain, schema_type)
493
+ return "" if entity.nil?
494
+
495
+ # See: metanorma/iso-10303-2#90
496
+ entity_type = case schema_type
497
+ when "module_arm"
498
+ "{{application object}}"
499
+ when "module_mim"
500
+ "{{entity data type}}"
501
+ when "resource", "business_object_model"
502
+ "{{entity data type}}"
503
+ else
504
+ raise Error.new("[suma] encountered unsupported schema_type")
505
+ end
506
+
507
+ if entity.subtype_of.empty?
508
+ "#{entity_type} " \
509
+ "that represents the " \
510
+ "#{entity_name_to_text(entity.id)} {{entity}}"
511
+ else
512
+ entity_subtypes = entity.subtype_of.map do |e|
513
+ "{{#{e.id}}}"
514
+ end
515
+
516
+ "#{entity_type} that is a type of " \
517
+ "#{entity_subtypes.join(' and ')} " \
518
+ "that represents the " \
519
+ "#{entity_name_to_text(entity.id)} {{entity}}"
520
+ end
521
+ end
522
+ # rubocop:enable Layout/LineLength
523
+
269
524
  def convert_express_xref(content, schema_domain)
270
525
  content.gsub(/<<express:(.*),(.*)>>/) do
271
526
  "{{<#{schema_domain}>" \
@@ -273,13 +528,8 @@ module Suma
273
528
  end
274
529
  end
275
530
 
276
- def get_concept_filename(concept)
277
- identifier = concept["data"]["identifier"]
278
- "#{sanitize_string(identifier)}.yaml"
279
- end
280
-
281
- def sanitize_string(str)
282
- str.gsub(" ", "_").gsub("/", "_").gsub(":", "_")
531
+ def id_from_designation(designation)
532
+ designation.gsub(" ", "_").gsub("/", "_").gsub(":", "_")
283
533
  end
284
534
  end
285
535
  end
@@ -57,7 +57,7 @@ module Suma
57
57
  # containing '(*text*)' inside
58
58
  comments = file_content.scan(/\(\*"(.*?)\n\*\)/m).map(&:first)
59
59
 
60
- if comments.count.positive?
60
+ if comments.any?
61
61
  content_without_comments = file_content.gsub(/\(\*".*?\n\*\)/m, "")
62
62
 
63
63
  # remove extra newlines
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "thor"
4
4
  require_relative "../utils"
5
+ require "expressir"
5
6
 
6
7
  module Suma
7
8
  module Cli
@@ -43,7 +44,6 @@ module Suma
43
44
  # Lazy-load dependencies only when this command is actually used
44
45
  require "expressir"
45
46
  require "ruby-progressbar"
46
- require_relative "../schema_config"
47
47
  require "pathname"
48
48
  end
49
49
 
@@ -74,7 +74,7 @@ module Suma
74
74
 
75
75
  # Load and initialize the schemas configuration
76
76
  def load_schemas_config(schemas_file_path)
77
- schemas_config = Suma::SchemaConfig::Config.from_yaml(File.read(schemas_file_path))
77
+ schemas_config = Expressir::SchemaManifest.from_yaml(File.read(schemas_file_path))
78
78
  # Ensure the config is initialized with the correct path to resolve relative paths
79
79
  schemas_config.set_initial_path(schemas_file_path.to_s)
80
80
  schemas_config
@@ -3,6 +3,7 @@
3
3
  require "metanorma/cli"
4
4
  require "metanorma/cli/collection"
5
5
  require "metanorma/collection/collection"
6
+ require "expressir"
6
7
 
7
8
  module Suma
8
9
  class CollectionManifest < Metanorma::Collection::Config::Manifest
@@ -36,7 +37,7 @@ module Suma
36
37
  end
37
38
 
38
39
  def export_schema_config(path)
39
- export_config = @schema_config || Suma::SchemaConfig::Config.new
40
+ export_config = @schema_config || Expressir::SchemaManifest.new
40
41
  return export_config unless entry
41
42
 
42
43
  entry.each do |x|
@@ -127,7 +128,7 @@ module Suma
127
128
  if File.basename(file) == "collection.yml"
128
129
  schemas_yaml_path = File.join(File.dirname(file), "schemas.yaml")
129
130
  if schemas_yaml_path && File.exist?(schemas_yaml_path)
130
- @schema_config = Suma::SchemaConfig::Config.from_file(schemas_yaml_path)
131
+ @schema_config = Expressir::SchemaManifest.from_file(schemas_yaml_path)
131
132
  end
132
133
  end
133
134
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "schema_config"
4
3
  require_relative "schema_collection"
5
4
  require_relative "utils"
6
5
  require_relative "collection_config"
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "expressir"
4
5
  # require "metanorma/cli"
5
- require_relative "schema_config"
6
6
 
7
7
  module Suma
8
8
  class SchemaAttachment
@@ -68,8 +68,8 @@ module Suma
68
68
 
69
69
  def to_config(path: nil)
70
70
  # return @config unless @config
71
- @config = SchemaConfig::Config.new
72
- @config.schemas << SchemaConfig::Schema.new(
71
+ @config = Expressir::SchemaManifest.new
72
+ @config.schemas << Expressir::SchemaManifestEntry.new(
73
73
  id: @schema.id,
74
74
  path: @schema.path,
75
75
  )
@@ -3,7 +3,7 @@
3
3
  require_relative "express_schema"
4
4
  require_relative "schema_attachment"
5
5
  require_relative "schema_document"
6
- require_relative "schema_config"
6
+ require "expressir"
7
7
  require_relative "utils"
8
8
 
9
9
  module Suma
@@ -19,7 +19,7 @@ module Suma
19
19
  @output_path_docs = Pathname.new(output_path_docs || Dir.pwd).expand_path
20
20
  @output_path_schemas = Pathname.new(output_path_schemas || Dir.pwd).expand_path
21
21
  @config = config
22
- @config ||= config_yaml && SchemaConfig::Config.from_file(config_yaml)
22
+ @config ||= config_yaml && Expressir::SchemaManifest.from_file(config_yaml)
23
23
  @manifest = manifest
24
24
  end
25
25
 
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.1.14"
4
+ VERSION = "0.1.16"
5
5
  end
data/lib/suma.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "expressir"
3
4
  require_relative "suma/version"
4
5
  require_relative "suma/processor"
5
6
 
data/suma.gemspec CHANGED
@@ -34,6 +34,7 @@ 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"
37
+ spec.add_dependency "glossarist", "~> 2.3.7"
37
38
  spec.add_dependency "lutaml-model", "~> 0.7"
38
39
  spec.add_dependency "metanorma-cli"
39
40
  spec.add_dependency "plurimath"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: suma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-06-20 00:00:00.000000000 Z
11
+ date: 2025-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: expressir
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: glossarist
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.7
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: lutaml-model
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -146,9 +160,6 @@ files:
146
160
  - lib/suma/processor.rb
147
161
  - lib/suma/schema_attachment.rb
148
162
  - lib/suma/schema_collection.rb
149
- - lib/suma/schema_config.rb
150
- - lib/suma/schema_config/config.rb
151
- - lib/suma/schema_config/schema.rb
152
163
  - lib/suma/schema_document.rb
153
164
  - lib/suma/site_config.rb
154
165
  - lib/suma/thor_ext.rb
@@ -1,147 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "lutaml/model"
4
- require_relative "schema"
5
- require_relative "../utils"
6
-
7
- module Suma
8
- module SchemaConfig
9
- class Config < Lutaml::Model::Serializable
10
- attribute :schemas, Schema, collection: true, initialize_empty: true
11
- attribute :path, Lutaml::Model::Type::String
12
- attr_accessor :output_path
13
-
14
- def initialize(**args)
15
- @path = path_relative_to_absolute(path) if path
16
- super
17
- end
18
-
19
- def base_path
20
- File.dirname(@path)
21
- end
22
-
23
- yaml do
24
- map "schemas", with: { from: :schemas_from_yaml, to: :schemas_to_yaml }
25
- end
26
-
27
- def self.from_file(path)
28
- from_yaml(File.read(path)).tap do |x|
29
- x.set_initial_path(path)
30
- end
31
- end
32
-
33
- def to_file(to_path = path)
34
- File.write(to_path, to_yaml)
35
- end
36
-
37
- def set_initial_path(new_path)
38
- @path = path_relative_to_absolute(new_path)
39
- schemas.each do |schema|
40
- schema.path = path_relative_to_absolute(schema.path)
41
- schema.container_path = File.expand_path(@path)
42
- end
43
- end
44
-
45
- def schemas_from_yaml(model, value)
46
- model.schemas = value.map do |k, v|
47
- Schema.new(id: k, path: path_relative_to_absolute(v["path"]))
48
- end
49
- end
50
-
51
- def schemas_to_yaml(model, doc)
52
- doc["schemas"] = model.schemas.sort_by(&:id).to_h do |schema|
53
- # We are outputting the schemas collection file to the directory where
54
- # the collection config is at (assumed to be Dir.pwd), not to the
55
- # directory we sourced the manifest from, e.g.
56
- # documents/iso-10303-41/schemas.yaml.
57
-
58
- # So the schema.container_path = @config.path is not
59
- # in fact needed, as the files are already absolute. This notion of
60
- # using @path to create relative paths was misconceived
61
- [
62
- schema.id,
63
- {
64
- "path" => path_absolute_to_relative(
65
- schema.path,
66
- model.output_path || Dir.pwd,
67
- ),
68
- },
69
- ]
70
- end
71
- end
72
-
73
- def path_relative_to_absolute(relative_path)
74
- eval_path = Pathname.new(relative_path)
75
- return relative_path if eval_path.absolute?
76
-
77
- # Or based on current working directory?
78
- return relative_path unless @path
79
-
80
- # ... but if this calculates path, we end up expanding it anyway
81
-
82
- Pathname.new(File.dirname(@path)).join(eval_path).expand_path.to_s
83
- end
84
-
85
- def path_absolute_to_relative(absolute_path, container_path)
86
- container_path ||= @path
87
- return absolute_path unless container_path
88
-
89
- p = Pathname.new(container_path)
90
- container = p.directory? ? p.to_s : p.dirname
91
- Pathname.new(absolute_path).relative_path_from(container).to_s
92
- end
93
-
94
- def update_path(new_path)
95
- if @path.nil?
96
- @path = new_path
97
- return @path
98
- end
99
-
100
- old_base_path = File.dirname(@path)
101
- new_base_path = File.dirname(new_path)
102
- update_schema_path(old_base_path, new_base_path)
103
-
104
- @path = new_path
105
- end
106
-
107
- def concat(another_config)
108
- unless another_config.is_a?(self.class)
109
- raise StandardError, "Can only concat a SchemaConfig::Config object."
110
- end
111
-
112
- # We need to update the relative paths when paths exist
113
- if path && another_config.path && path != another_config.path
114
- new_config = another_config.dup
115
- new_config.update_path(path)
116
- end
117
-
118
- schemas.concat(another_config.schemas)
119
- end
120
-
121
- def save_to_path(filename)
122
- new_config = dup.tap do |c|
123
- c.path = filename
124
- c.update_path(File.dirname(filename))
125
- c.output_path = filename
126
- end
127
-
128
- File.open(filename, "w") do |f|
129
- Utils.log "Writing #{filename}..."
130
- f.write(new_config.to_yaml)
131
- Utils.log "Done."
132
- end
133
- end
134
-
135
- def update_schema_path(old_base_path, new_base_path)
136
- schemas.each do |schema|
137
- schema_path = Pathname.new(schema.path)
138
- next if schema_path.absolute?
139
-
140
- schema_path = (Pathname.new(old_base_path) + schema_path).cleanpath
141
- # This is the new relative schema_path
142
- schema.path = schema_path.relative_path_from(new_base_path)
143
- end
144
- end
145
- end
146
- end
147
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "lutaml/model"
4
-
5
- module Suma
6
- module SchemaConfig
7
- class Schema < Lutaml::Model::Serializable
8
- attribute :id, Lutaml::Model::Type::String
9
- attribute :path, Lutaml::Model::Type::String
10
- # attribute :schemas_only, Lutaml::Model::Type::Boolean
11
-
12
- # container_path is a copy of Suma::SchemaConfig::Config.path,
13
- # used to resolve the path of each schema within
14
- # Suma::SchemaConfig::Config.schemas,
15
- # when Suma::SchemaConfig::Config.schemas is recursively flattened
16
- attr_accessor :container_path
17
- end
18
- end
19
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "schema_config/schema"
4
- require_relative "schema_config/config"