glossarist 2.8.1 → 2.8.4

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.rubocop_todo.yml +413 -63
  4. data/CLAUDE.md +1 -1
  5. data/Gemfile +1 -1
  6. data/README.adoc +4 -1
  7. data/glossarist.gemspec +2 -1
  8. data/lib/glossarist/bibliography_data.rb +41 -0
  9. data/lib/glossarist/bibliography_entry.rb +13 -0
  10. data/lib/glossarist/citation.rb +8 -2
  11. data/lib/glossarist/cli/export_command.rb +10 -5
  12. data/lib/glossarist/cli/validate_command.rb +21 -5
  13. data/lib/glossarist/collection.rb +2 -2
  14. data/lib/glossarist/collections/bibliography_collection.rb +2 -1
  15. data/lib/glossarist/collections/collection.rb +2 -2
  16. data/lib/glossarist/collections/localization_collection.rb +4 -4
  17. data/lib/glossarist/concept_collector.rb +6 -6
  18. data/lib/glossarist/concept_document.rb +2 -3
  19. data/lib/glossarist/concept_manager.rb +6 -8
  20. data/lib/glossarist/concept_set.rb +4 -4
  21. data/lib/glossarist/concept_store.rb +84 -0
  22. data/lib/glossarist/dataset_validator.rb +2 -1
  23. data/lib/glossarist/gcr_package_definition.rb +37 -0
  24. data/lib/glossarist/gcr_statistics.rb +2 -2
  25. data/lib/glossarist/glossary_definition.rb +1 -1
  26. data/lib/glossarist/glossary_store.rb +201 -0
  27. data/lib/glossarist/managed_concept_collection.rb +2 -2
  28. data/lib/glossarist/managed_concept_data.rb +2 -0
  29. data/lib/glossarist/rdf/gloss_citation.rb +8 -4
  30. data/lib/glossarist/rdf/gloss_concept.rb +13 -4
  31. data/lib/glossarist/rdf/gloss_concept_date.rb +4 -2
  32. data/lib/glossarist/rdf/gloss_concept_reference.rb +6 -3
  33. data/lib/glossarist/rdf/gloss_concept_source.rb +6 -3
  34. data/lib/glossarist/rdf/gloss_designation.rb +63 -26
  35. data/lib/glossarist/rdf/gloss_grammar_info.rb +19 -9
  36. data/lib/glossarist/rdf/gloss_locality.rb +6 -3
  37. data/lib/glossarist/rdf/gloss_localized_concept.rb +14 -7
  38. data/lib/glossarist/rdf/gloss_non_verbal_rep.rb +9 -4
  39. data/lib/glossarist/rdf/gloss_pronunciation.rb +13 -6
  40. data/lib/glossarist/rdf/gloss_reference.rb +8 -4
  41. data/lib/glossarist/rdf/namespaces.rb +3 -2
  42. data/lib/glossarist/rdf/relationship_predicates.rb +79 -0
  43. data/lib/glossarist/rdf/v3/configuration.rb +0 -2
  44. data/lib/glossarist/rdf/v3.rb +4 -43
  45. data/lib/glossarist/rdf.rb +26 -23
  46. data/lib/glossarist/register_data.rb +68 -18
  47. data/lib/glossarist/schema_migration.rb +8 -5
  48. data/lib/glossarist/sts/importer.rb +0 -1
  49. data/lib/glossarist/transforms/concept_to_gloss_transform.rb +57 -72
  50. data/lib/glossarist/v2/concept_data.rb +2 -1
  51. data/lib/glossarist/v2/concept_document.rb +1 -1
  52. data/lib/glossarist/v2/configuration.rb +0 -2
  53. data/lib/glossarist/v2/managed_concept_data.rb +1 -0
  54. data/lib/glossarist/v2.rb +12 -12
  55. data/lib/glossarist/v3/concept_data.rb +2 -1
  56. data/lib/glossarist/v3/concept_document.rb +1 -1
  57. data/lib/glossarist/v3/configuration.rb +0 -2
  58. data/lib/glossarist/v3/managed_concept_data.rb +1 -0
  59. data/lib/glossarist/v3.rb +16 -16
  60. data/lib/glossarist/validation/asset_index.rb +2 -2
  61. data/lib/glossarist/validation/bibliography_index.rb +11 -8
  62. data/lib/glossarist/validation/rules/asciidoc_xref_rule.rb +2 -1
  63. data/lib/glossarist/validation/rules/authoritative_source_rule.rb +1 -1
  64. data/lib/glossarist/validation/rules/bibliography_yaml_rule.rb +1 -1
  65. data/lib/glossarist/validation/rules/citation_completeness_rule.rb +1 -1
  66. data/lib/glossarist/validation/rules/concept_count_rule.rb +2 -3
  67. data/lib/glossarist/validation/rules/concept_id_rule.rb +0 -1
  68. data/lib/glossarist/validation/rules/concept_id_uniqueness_rule.rb +0 -1
  69. data/lib/glossarist/validation/rules/concept_mention_rule.rb +1 -2
  70. data/lib/glossarist/validation/rules/concept_status_rule.rb +1 -2
  71. data/lib/glossarist/validation/rules/concept_uri_rule.rb +1 -1
  72. data/lib/glossarist/validation/rules/date_type_rule.rb +5 -3
  73. data/lib/glossarist/validation/rules/date_validity_rule.rb +1 -1
  74. data/lib/glossarist/validation/rules/definition_content_rule.rb +1 -2
  75. data/lib/glossarist/validation/rules/domain_target_rule.rb +1 -1
  76. data/lib/glossarist/validation/rules/duplicate_term_rule.rb +1 -2
  77. data/lib/glossarist/validation/rules/entry_status_rule.rb +1 -2
  78. data/lib/glossarist/validation/rules/filename_id_rule.rb +2 -3
  79. data/lib/glossarist/validation/rules/image_reference_rule.rb +3 -2
  80. data/lib/glossarist/validation/rules/l10n_uuid_integrity_rule.rb +1 -2
  81. data/lib/glossarist/validation/rules/language_coverage_rule.rb +1 -2
  82. data/lib/glossarist/validation/rules/language_list_rule.rb +2 -3
  83. data/lib/glossarist/validation/rules/locality_completeness_rule.rb +3 -1
  84. data/lib/glossarist/validation/rules/localization_consistency_rule.rb +1 -1
  85. data/lib/glossarist/validation/rules/localization_presence_rule.rb +0 -1
  86. data/lib/glossarist/validation/rules/model_validity_rule.rb +1 -1
  87. data/lib/glossarist/validation/rules/orphaned_bibliography_rule.rb +2 -1
  88. data/lib/glossarist/validation/rules/orphaned_images_rule.rb +6 -4
  89. data/lib/glossarist/validation/rules/orphaned_l10n_files_rule.rb +1 -2
  90. data/lib/glossarist/validation/rules/preferred_term_rule.rb +1 -2
  91. data/lib/glossarist/validation/rules/related_concept_cycle_rule.rb +2 -2
  92. data/lib/glossarist/validation/rules/related_concept_rule.rb +1 -2
  93. data/lib/glossarist/validation/rules/related_concept_symmetry_rule.rb +1 -1
  94. data/lib/glossarist/validation/rules/related_concept_target_rule.rb +1 -1
  95. data/lib/glossarist/validation/rules/source_type_rule.rb +2 -2
  96. data/lib/glossarist/validation/rules/source_urn_format_rule.rb +2 -2
  97. data/lib/glossarist/validation/rules/terms_presence_rule.rb +1 -1
  98. data/lib/glossarist/validation/rules/uuid_format_rule.rb +1 -1
  99. data/lib/glossarist/validation/rules.rb +18 -49
  100. data/lib/glossarist/version.rb +1 -1
  101. data/lib/glossarist.rb +13 -11
  102. data/scripts/migrate_dataset.rb +14 -8
  103. data/scripts/migrate_isotc204_to_v3.rb +3 -1
  104. data/scripts/migrate_isotc211_to_v3.rb +5 -3
  105. data/scripts/migrate_osgeo_to_v3.rb +4 -2
  106. data/scripts/upgrade_dataset_to_v3.rb +0 -0
  107. metadata +24 -11
  108. data/lib/glossarist/rdf/ext/jsonld_transform_ext.rb +0 -208
  109. data/lib/glossarist/rdf/ext/mapping_ext.rb +0 -37
  110. data/lib/glossarist/rdf/ext/mapping_rule_ext.rb +0 -27
  111. data/lib/glossarist/rdf/ext/member_rule_ext.rb +0 -34
  112. data/lib/glossarist/rdf/ext/turtle_transform_ext.rb +0 -222
  113. data/lib/glossarist/rdf/ext.rb +0 -39
  114. data/lib/glossarist/rdf/relationships.rb +0 -19
@@ -26,7 +26,7 @@ module Glossarist
26
26
  "related concept #{idx + 1} has invalid type '#{rel.type}'",
27
27
  code: code, severity: severity,
28
28
  location: fname,
29
- suggestion: "Use one of: #{VALID_TYPES.join(', ')}",
29
+ suggestion: "Use one of: #{VALID_TYPES.join(', ')}"
30
30
  )
31
31
  end
32
32
  end
@@ -37,4 +37,3 @@ module Glossarist
37
37
  end
38
38
  end
39
39
  end
40
-
@@ -40,7 +40,7 @@ module Glossarist
40
40
  next unless target_id
41
41
 
42
42
  targets = index[target_id]
43
- next if targets && targets.any? { |r| r.type == inverse }
43
+ next if targets&.any? { |r| r.type == inverse }
44
44
 
45
45
  issues << issue(
46
46
  "#{concept_id}: #{rel.type} #{target_id} but #{target_id} has no #{inverse} back-link",
@@ -6,7 +6,7 @@ module Glossarist
6
6
  # Verifies that related concept refs point to concepts that exist
7
7
  # in the dataset (for local refs) or have valid source/URN (for external).
8
8
  class RelatedConceptTargetRule < Base
9
- URN_RE = %r{\Aurn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,\-.:=@;$_!*'%/?#]+\z}i.freeze
9
+ URN_RE = %r{\Aurn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,\-.:=@;$_!*'%/?#]+\z}i
10
10
 
11
11
  def code = "GLS-110"
12
12
  def category = :references
@@ -27,7 +27,7 @@ module Glossarist
27
27
  "source #{idx + 1} has invalid type '#{source.type}'",
28
28
  code: "GLS-202", severity: severity,
29
29
  location: fname,
30
- suggestion: "Use one of: #{VALID_TYPES.join(', ')}",
30
+ suggestion: "Use one of: #{VALID_TYPES.join(', ')}"
31
31
  )
32
32
  end
33
33
 
@@ -37,7 +37,7 @@ module Glossarist
37
37
  "source #{idx + 1} has invalid status '#{source.status}'",
38
38
  code: "GLS-203", severity: severity,
39
39
  location: fname,
40
- suggestion: "Use one of: #{VALID_STATUSES.join(', ')}",
40
+ suggestion: "Use one of: #{VALID_STATUSES.join(', ')}"
41
41
  )
42
42
  end
43
43
 
@@ -6,7 +6,7 @@ module Glossarist
6
6
  # Validates that every URN-format source in citations and references
7
7
  # follows a recognized scheme (iso, iec, itu, etc).
8
8
  class SourceUrnFormatRule < Base
9
- URN_RE = %r{\Aurn:([a-z0-9][a-z0-9-]{0,31}):(.+)\z}i.freeze
9
+ URN_RE = %r{\Aurn:([a-z0-9][a-z0-9-]{0,31}):(.+)\z}i
10
10
 
11
11
  KNOWN_SCHEMES = %w[
12
12
  iso iec itu iso:std:iso iso:std:iec
@@ -27,7 +27,7 @@ module Glossarist
27
27
  issues = []
28
28
 
29
29
  all_refs(concept).each_with_index do |ref_str, idx|
30
- next unless ref_str && ref_str.start_with?("urn:")
30
+ next unless ref_str&.start_with?("urn:")
31
31
 
32
32
  match = URN_RE.match(ref_str)
33
33
  unless match
@@ -27,7 +27,7 @@ module Glossarist
27
27
  "#{fname}/#{lang}: must have at least 1 term",
28
28
  code: code, severity: severity,
29
29
  location: "#{fname}/#{lang}",
30
- suggestion: "Add at least one term/designation to this localization",
30
+ suggestion: "Add at least one term/designation to this localization"
31
31
  )
32
32
  end
33
33
 
@@ -4,7 +4,7 @@ module Glossarist
4
4
  module Validation
5
5
  module Rules
6
6
  class UuidFormatRule < Base
7
- UUID_RE = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i.freeze
7
+ UUID_RE = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
8
8
 
9
9
  def code = "GLS-016"
10
10
  def category = :integrity
@@ -1,52 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rules/base"
4
- require_relative "rules/registry"
5
- require_relative "rules/dataset_context"
6
- require_relative "rules/gcr_context"
7
- require_relative "rules/concept_context"
3
+ module Glossarist
4
+ module Validation
5
+ module Rules
6
+ autoload :Base, "glossarist/validation/rules/base"
7
+ autoload :Registry, "glossarist/validation/rules/registry"
8
+ autoload :DatasetContext, "glossarist/validation/rules/dataset_context"
9
+ autoload :GcrContext, "glossarist/validation/rules/gcr_context"
10
+ autoload :ConceptContext, "glossarist/validation/rules/concept_context"
8
11
 
9
- # Load all rule definitions
10
- require_relative "rules/concept_id_rule"
11
- require_relative "rules/concept_id_uniqueness_rule"
12
- require_relative "rules/localization_presence_rule"
13
- require_relative "rules/entry_status_rule"
14
- require_relative "rules/asciidoc_xref_rule"
15
- require_relative "rules/image_reference_rule"
16
- require_relative "rules/concept_mention_rule"
17
- require_relative "rules/concept_count_rule"
18
- require_relative "rules/language_list_rule"
19
- require_relative "rules/language_coverage_rule"
20
- require_relative "rules/filename_id_rule"
21
- require_relative "rules/l10n_uuid_integrity_rule"
22
- require_relative "rules/orphaned_l10n_files_rule"
23
- require_relative "rules/orphaned_bibliography_rule"
24
- require_relative "rules/orphaned_images_rule"
25
- require_relative "rules/definition_content_rule"
26
- require_relative "rules/preferred_term_rule"
27
- require_relative "rules/duplicate_term_rule"
28
- require_relative "rules/citation_completeness_rule"
29
- require_relative "rules/authoritative_source_rule"
30
- require_relative "rules/related_concept_rule"
31
- require_relative "rules/concept_status_rule"
32
- require_relative "rules/source_type_rule"
33
- require_relative "rules/terms_presence_rule"
34
- require_relative "rules/bibliography_yaml_rule"
35
- require_relative "rules/concept_uri_rule"
36
- require_relative "rules/related_concept_symmetry_rule"
37
- require_relative "rules/related_concept_cycle_rule"
38
- require_relative "rules/designation_status_rule"
39
- require_relative "rules/date_type_rule"
40
- require_relative "rules/language_code_format_rule"
41
- require_relative "rules/designation_type_rule"
42
- require_relative "rules/date_validity_rule"
43
- require_relative "rules/schema_version_rule"
44
- require_relative "rules/ref_shape_rule"
45
- require_relative "rules/locality_completeness_rule"
46
- require_relative "rules/domain_ref_rule"
47
- require_relative "rules/uuid_format_rule"
48
- require_relative "rules/localization_consistency_rule"
49
- require_relative "rules/related_concept_target_rule"
50
- require_relative "rules/domain_target_rule"
51
- require_relative "rules/source_urn_format_rule"
52
- require_relative "rules/model_validity_rule"
12
+ RULES_DIR = File.join(__dir__, "rules")
13
+
14
+ # Eagerly load all rule files so they self-register via Base.inherited.
15
+ # Adding a new rule file requires zero changes here — just drop it in.
16
+ Dir.glob(File.join(RULES_DIR, "*_rule.rb")).each do |path|
17
+ require path
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- VERSION = "2.8.1"
7
+ VERSION = "2.8.4"
8
8
  end
data/lib/glossarist.rb CHANGED
@@ -11,6 +11,8 @@ module Glossarist
11
11
  autoload :Asset, "glossarist/asset"
12
12
  autoload :AssetReference, "glossarist/asset_reference"
13
13
  autoload :BibliographicReference, "glossarist/bibliographic_reference"
14
+ autoload :BibliographyData, "glossarist/bibliography_data"
15
+ autoload :BibliographyEntry, "glossarist/bibliography_entry"
14
16
  autoload :Citation, "glossarist/citation"
15
17
  autoload :CLI, "glossarist/cli"
16
18
  autoload :CollectionConfig, "glossarist/collection_config"
@@ -27,11 +29,13 @@ module Glossarist
27
29
  autoload :ConceptManager, "glossarist/concept_manager"
28
30
  autoload :ConceptSet, "glossarist/concept_set"
29
31
  autoload :ConceptSource, "glossarist/concept_source"
32
+ autoload :ConceptStore, "glossarist/concept_store"
30
33
  autoload :ConceptValidator, "glossarist/concept_validator"
31
- autoload :ConceptCollector, "glossarist/concept_collector"
32
- autoload :ConceptComparator, "glossarist/concept_comparator"
33
- autoload :ComparisonResult, "glossarist/comparison_result"
34
- autoload :ConceptDiff, "glossarist/concept_diff"
34
+ autoload :ConceptCollector, "glossarist/concept_collector"
35
+ autoload :ConceptComparator, "glossarist/concept_comparator"
36
+ autoload :ContextConfiguration, "glossarist/context_configuration"
37
+ autoload :ComparisonResult, "glossarist/comparison_result"
38
+ autoload :ConceptDiff, "glossarist/concept_diff"
35
39
  autoload :ConceptDocument, "glossarist/concept_document"
36
40
  autoload :ConceptEnricher, "glossarist/concept_enricher"
37
41
  autoload :Config, "glossarist/config"
@@ -41,10 +45,11 @@ module Glossarist
41
45
  autoload :Designation, "glossarist/designation"
42
46
  autoload :Error, "glossarist/error"
43
47
  autoload :GcrPackage, "glossarist/gcr_package"
48
+ autoload :GcrPackageDefinition, "glossarist/gcr_package_definition"
44
49
  autoload :GcrMetadata, "glossarist/gcr_metadata"
45
50
  autoload :GcrStatistics, "glossarist/gcr_statistics"
46
51
  autoload :GcrValidator, "glossarist/gcr_validator"
47
- autoload :InvalidTypeError, "glossarist/error/invalid_type_error"
52
+ autoload :InvalidTypeError, "glossarist/error/invalid_type_error"
48
53
  autoload :InvalidLanguageCodeError,
49
54
  "glossarist/error/invalid_language_code_error"
50
55
  autoload :ParseError, "glossarist/error/parse_error"
@@ -70,13 +75,10 @@ module Glossarist
70
75
  autoload :V1, "glossarist/v1"
71
76
  autoload :V2, "glossarist/v2"
72
77
  autoload :V3, "glossarist/v3"
73
- end
74
-
75
- require_relative "glossarist/version"
76
- require_relative "glossarist/collections"
77
- require_relative "glossarist/glossary_definition"
78
+ autoload :VERSION, "glossarist/version"
79
+ autoload :GlossaryDefinition, "glossarist/glossary_definition"
80
+ autoload :GlossaryStore, "glossarist/glossary_store"
78
81
 
79
- module Glossarist
80
82
  LANG_CODES = %w[eng ara deu fra spa ita jpn kor pol por srp swe zho rus fin
81
83
  dan nld msa nob nno].freeze
82
84
 
@@ -45,7 +45,7 @@ def add_subject_area_concepts(collection)
45
45
  end
46
46
  end
47
47
 
48
- existing_ids = collection.map { |c| c.data.id }.to_set
48
+ existing_ids = collection.to_set { |c| c.data.id }
49
49
 
50
50
  areas.each_key do |area_id|
51
51
  next if existing_ids.include?(area_id)
@@ -83,7 +83,8 @@ def add_subject_area_concepts(collection)
83
83
  ),
84
84
  )
85
85
 
86
- mc.related = [Glossarist::RelatedConcept.new(type: "broader", content: area_id)]
86
+ mc.related = [Glossarist::RelatedConcept.new(type: "broader",
87
+ content: area_id)]
87
88
 
88
89
  section_code = parts.length > 1 ? parts[0..1].join("-") : parts[0]
89
90
  l10n = build_domain_localization(section_id, section_code, "eng")
@@ -94,7 +95,7 @@ def add_subject_area_concepts(collection)
94
95
  end
95
96
  end
96
97
 
97
- def build_domain_localization(id, label, lang_code)
98
+ def build_domain_localization(id, _label, lang_code)
98
99
  cd = Glossarist::ConceptData.new(
99
100
  id: id,
100
101
  language_code: lang_code,
@@ -133,6 +134,7 @@ if add_iev_domains
133
134
 
134
135
  identifier = concept.data.id.to_s
135
136
  next if identifier.empty? || identifier.start_with?("area-", "section-")
137
+
136
138
  parts = identifier.split("-")
137
139
  next unless parts.length >= 2
138
140
 
@@ -166,11 +168,15 @@ end
166
168
 
167
169
  # Copy register.yaml if present
168
170
  register_src = File.join(File.dirname(source_dir), "register.yaml")
169
- if File.exist?(register_src) && !File.exist?(File.join(output_dir, "..", "register.yaml"))
170
- register_dst_dir = is_managed_format ? File.dirname(output_dir) : output_dir
171
- register_dst = if File.exist?(File.join(File.dirname(source_dir), "register.yaml"))
172
- File.join(is_managed_format ? File.dirname(output_dir) : File.dirname(output_dir), "register.yaml")
173
- end
171
+ if File.exist?(register_src) && !File.exist?(File.join(output_dir, "..",
172
+ "register.yaml"))
173
+ is_managed_format ? File.dirname(output_dir) : output_dir
174
+ register_dst = if File.exist?(File.join(File.dirname(source_dir),
175
+ "register.yaml"))
176
+ File.join(
177
+ is_managed_format ? File.dirname(output_dir) : File.dirname(output_dir), "register.yaml"
178
+ )
179
+ end
174
180
  if register_dst
175
181
  FileUtils.mkdir_p(File.dirname(register_dst))
176
182
  FileUtils.cp(register_src, register_dst) unless register_src == register_dst
@@ -70,7 +70,9 @@ collection.each do |concept|
70
70
 
71
71
  # Add broader relation to section
72
72
  concept.related ||= []
73
- unless concept.related.any? { |r| r.type == "broader" && r.ref&.id == section_uri }
73
+ unless concept.related.any? do |r|
74
+ r.type == "broader" && r.ref&.id == section_uri
75
+ end
74
76
  concept.related << Glossarist::RelatedConcept.new(
75
77
  type: "broader",
76
78
  content: section_uri,
@@ -26,13 +26,13 @@ ISO_SOURCE_URN = "urn:iso:std:iso"
26
26
  def extract_domain_id(ref_text)
27
27
  # Match various ISO reference patterns
28
28
  patterns = [
29
- %r{ISO/IEC/IEEE\s+([\d-]+)}, # ISO/IEC/IEEE 24765:2017
29
+ %r{ISO/IEC/IEEE\s+([\d-]+)}, # ISO/IEC/IEEE 24765:2017
30
30
  %r{ISO/IEC\s+([\d-]+)}, # ISO/IEC 19501:2005
31
31
  %r{ISO/TS\s+([\d-]+)}, # ISO/TS 19130:2010
32
32
  %r{ISO/TR\s+([\d-]+)}, # ISO/TR 19120:2001
33
33
  %r{ISO/IEC\s+Guide\s+([\d-]+)}, # ISO/IEC Guide 98-3:2008
34
34
  %r{ISO\s+DIS\s+([\d-]+)}, # ISO DIS 19123-1:2022
35
- %r{ISO\s+([\d]+-?[\d]*)}, # ISO 19136-1:2020
35
+ %r{ISO\s+(\d+-?\d*)}, # ISO 19136-1:2020
36
36
  ]
37
37
 
38
38
  patterns.each do |pat|
@@ -95,7 +95,9 @@ collection.each do |concept|
95
95
 
96
96
  # Add broader relation to domain concept
97
97
  concept.related ||= []
98
- unless concept.related.any? { |r| r.type == "broader" && r.ref&.id == domain_id }
98
+ unless concept.related.any? do |r|
99
+ r.type == "broader" && r.ref&.id == domain_id
100
+ end
99
101
  concept.related << Glossarist::RelatedConcept.new(
100
102
  type: "broader",
101
103
  content: domain_id,
@@ -32,7 +32,7 @@ def extract_domain_id(ref_text)
32
32
  %r{ISO/IEC\s+([\d-]+)},
33
33
  %r{ISO/TS\s+([\d-]+)},
34
34
  %r{ISO/TR\s+([\d-]+)},
35
- %r{ISO\s+([\d]+-?[\d]*)},
35
+ %r{ISO\s+(\d+-?\d*)},
36
36
  ]
37
37
 
38
38
  patterns.each do |pat|
@@ -95,7 +95,9 @@ collection.each do |concept|
95
95
 
96
96
  # Add broader relation to domain concept
97
97
  concept.related ||= []
98
- unless concept.related.any? { |r| r.type == "broader" && r.ref&.id == domain_id }
98
+ unless concept.related.any? do |r|
99
+ r.type == "broader" && r.ref&.id == domain_id
100
+ end
99
101
  concept.related << Glossarist::RelatedConcept.new(
100
102
  type: "broader",
101
103
  content: domain_id,
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glossarist
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-27 00:00:00.000000000 Z
11
+ date: 2026-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.8.5
19
+ version: 0.8.15
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.8.5
26
+ version: 0.8.15
27
+ - !ruby/object:Gem::Dependency
28
+ name: lutaml-store
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: paint
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -162,6 +176,8 @@ files:
162
176
  - lib/glossarist/asset.rb
163
177
  - lib/glossarist/asset_reference.rb
164
178
  - lib/glossarist/bibliographic_reference.rb
179
+ - lib/glossarist/bibliography_data.rb
180
+ - lib/glossarist/bibliography_entry.rb
165
181
  - lib/glossarist/citation.rb
166
182
  - lib/glossarist/cli.rb
167
183
  - lib/glossarist/cli/compare_command.rb
@@ -195,6 +211,7 @@ files:
195
211
  - lib/glossarist/concept_reference.rb
196
212
  - lib/glossarist/concept_set.rb
197
213
  - lib/glossarist/concept_source.rb
214
+ - lib/glossarist/concept_store.rb
198
215
  - lib/glossarist/concept_validator.rb
199
216
  - lib/glossarist/config.rb
200
217
  - lib/glossarist/context_configuration.rb
@@ -218,9 +235,11 @@ files:
218
235
  - lib/glossarist/error/parse_error.rb
219
236
  - lib/glossarist/gcr_metadata.rb
220
237
  - lib/glossarist/gcr_package.rb
238
+ - lib/glossarist/gcr_package_definition.rb
221
239
  - lib/glossarist/gcr_statistics.rb
222
240
  - lib/glossarist/gcr_validator.rb
223
241
  - lib/glossarist/glossary_definition.rb
242
+ - lib/glossarist/glossary_store.rb
224
243
  - lib/glossarist/locality.rb
225
244
  - lib/glossarist/localized_concept.rb
226
245
  - lib/glossarist/managed_concept.rb
@@ -229,12 +248,6 @@ files:
229
248
  - lib/glossarist/non_verb_rep.rb
230
249
  - lib/glossarist/pronunciation.rb
231
250
  - lib/glossarist/rdf.rb
232
- - lib/glossarist/rdf/ext.rb
233
- - lib/glossarist/rdf/ext/jsonld_transform_ext.rb
234
- - lib/glossarist/rdf/ext/mapping_ext.rb
235
- - lib/glossarist/rdf/ext/mapping_rule_ext.rb
236
- - lib/glossarist/rdf/ext/member_rule_ext.rb
237
- - lib/glossarist/rdf/ext/turtle_transform_ext.rb
238
251
  - lib/glossarist/rdf/gloss_citation.rb
239
252
  - lib/glossarist/rdf/gloss_concept.rb
240
253
  - lib/glossarist/rdf/gloss_concept_date.rb
@@ -258,7 +271,7 @@ files:
258
271
  - lib/glossarist/rdf/namespaces/rdf_namespace.rb
259
272
  - lib/glossarist/rdf/namespaces/skos_namespace.rb
260
273
  - lib/glossarist/rdf/namespaces/skosxl_namespace.rb
261
- - lib/glossarist/rdf/relationships.rb
274
+ - lib/glossarist/rdf/relationship_predicates.rb
262
275
  - lib/glossarist/rdf/v3.rb
263
276
  - lib/glossarist/rdf/v3/configuration.rb
264
277
  - lib/glossarist/reference_extractor.rb
@@ -1,208 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- # Extends Lutaml::JsonLd::Transform to handle:
6
- # - Multiple rdf:type values
7
- # - URI-valued predicates (as: :uri)
8
- # - Linking predicates for members (link option)
9
- # - Recursive context/namespace collection
10
- # - Recursive graph document building
11
- module Lutaml
12
- module JsonLd
13
- class Transform < Lutaml::Rdf::Transform
14
- def model_to_data(instance, _format, options = {})
15
- mapping = extract_mapping(options)
16
- return {} unless mapping
17
-
18
- if mapping.rdf_members.any?
19
- build_graph_document(mapping, instance)
20
- else
21
- build_resource_object(mapping, instance)
22
- end
23
- end
24
-
25
- def data_to_model(data, _format, options = {})
26
- mapping = extract_mapping(options)
27
- return model_class.new unless mapping
28
-
29
- hash = data.is_a?(String) ? JSON.parse(data) : data
30
-
31
- if hash.key?("@graph") && hash["@graph"].is_a?(Array) && !hash["@graph"].empty?
32
- graph_data = hash["@graph"]
33
- first = graph_data.first
34
- hash = first.is_a?(Hash) ? first : {}
35
- end
36
-
37
- hash = strip_jsonld_keywords(hash)
38
-
39
- attrs = {}
40
- mapping.rdf_predicates.each do |rule|
41
- value = hash[rule.predicate_name]
42
- next if value.nil?
43
-
44
- attrs[rule.to] = if rule.lang_tagged && value.is_a?(Hash)
45
- flatten_language_map(value)
46
- else
47
- value
48
- end
49
- end
50
-
51
- build_instance(attrs, options)
52
- end
53
-
54
- private
55
-
56
- def extract_mapping(options)
57
- options[:mappings] || mappings_for(:jsonld, lutaml_register)
58
- end
59
-
60
- def build_graph_document(mapping, instance)
61
- context = build_merged_context_recursive(mapping, instance)
62
- graph = collect_resources(mapping, instance)
63
-
64
- { "@context" => context, "@graph" => graph }
65
- end
66
-
67
- def collect_resources(mapping, instance)
68
- graph = []
69
-
70
- if mapping.rdf_subject
71
- resource = build_resource_data(mapping, instance)
72
- graph << resource unless resource.empty?
73
- end
74
-
75
- mapping.rdf_members.each do |member_rule|
76
- collection = Array(instance.public_send(member_rule.attr_name))
77
- collection.each do |member|
78
- member_mapping = member.class.mappings[:jsonld]
79
- next unless member_mapping
80
-
81
- resource = build_resource_data(member_mapping, member)
82
- graph << resource unless resource.empty?
83
-
84
- # Recurse into child members
85
- child_resources = collect_resources(member_mapping, member)
86
- # Skip the first entry (already added above) and any empty resources
87
- child_resources[1..-1].each { |r| graph << r }
88
- end
89
- end
90
-
91
- graph
92
- end
93
-
94
- def build_merged_context_recursive(mapping, instance)
95
- context_hash = build_context_from_mapping(mapping).to_hash
96
-
97
- mapping.rdf_members.each do |member_rule|
98
- collection = Array(instance.public_send(member_rule.attr_name))
99
- next if collection.empty?
100
-
101
- collection.map(&:class).uniq.each do |klass|
102
- member_mapping = klass.mappings[:jsonld]
103
- next unless member_mapping
104
-
105
- context_hash.merge!(build_context_from_mapping(member_mapping).to_hash)
106
-
107
- # Recurse into child members
108
- child_ctx = build_merged_context_recursive(member_mapping, collection.first)
109
- context_hash.merge!(child_ctx)
110
- end
111
- end
112
-
113
- context_hash
114
- end
115
-
116
- def build_context_from_mapping(mapping)
117
- context = Context.new
118
- mapping.namespace_set.each { |ns| context.prefix(ns) }
119
- mapping.rdf_predicates.each do |pred|
120
- if pred.lang_tagged
121
- context.term(pred.predicate_name,
122
- id: pred.uri,
123
- container: :language)
124
- else
125
- context.term(pred.predicate_name, id: pred.uri)
126
- end
127
- end
128
- context
129
- end
130
-
131
- def build_resource_object(mapping, instance)
132
- context = build_context_from_mapping(mapping).to_hash
133
- data = build_resource_data(mapping, instance)
134
- { "@context" => context }.merge(data)
135
- end
136
-
137
- def build_resource_data(mapping, instance)
138
- result = {}
139
-
140
- if mapping.rdf_types.any?
141
- types = mapping.rdf_types.map do |t|
142
- mapping.namespace_set.resolve_compact_iri(t)
143
- end
144
- result["@type"] = types.length == 1 ? types.first : types
145
- end
146
-
147
- if mapping.rdf_subject
148
- result["@id"] = resolve_subject_uri(mapping, instance)
149
- end
150
-
151
- mapping.rdf_predicates.each do |rule|
152
- value = instance.public_send(rule.to)
153
- next if value.nil?
154
- next if value.is_a?(String) && value.empty?
155
-
156
- result[rule.predicate_name] = if rule.lang_tagged
157
- build_language_map(value)
158
- else
159
- serialize_rdf_value(value, rule, mapping)
160
- end
161
- end
162
-
163
- # Emit variable-predicate relationship triples
164
- if instance.is_a?(Glossarist::Rdf::Relationships)
165
- Array(instance.relationship_triples).each do |pred_uri, obj_uri|
166
- key = pred_uri.split("#").last.split("/").last
167
- result[key] = { "@id" => obj_uri }
168
- end
169
- end
170
-
171
- result
172
- end
173
-
174
- def build_language_map(values)
175
- case values
176
- when Array
177
- map = {}
178
- values.each do |v|
179
- lang = extract_language(v)
180
- map[lang] = v.to_s if lang
181
- end
182
- map.empty? ? nil : map
183
- else
184
- lang = extract_language(values)
185
- lang ? { lang => values.to_s } : values.to_s
186
- end
187
- end
188
-
189
- def flatten_language_map(lang_map)
190
- lang_map.values
191
- end
192
-
193
- def serialize_rdf_value(value, rule = nil, mapping = nil)
194
- case value
195
- when Array then value.map { |v| serialize_rdf_value(v, rule, mapping) }
196
- when Integer, Float, TrueClass, FalseClass then value
197
- else value.to_s
198
- end
199
- end
200
-
201
- def strip_jsonld_keywords(data)
202
- return data unless data.is_a?(Hash)
203
-
204
- data.reject { |key, _| key.start_with?("@") }
205
- end
206
- end
207
- end
208
- end