glossarist 2.8.14 → 2.8.15

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: 411f9be5f441296d22581729529924c738bdee6538d99c9085c7b1dc0bbaacc7
4
- data.tar.gz: a36be3a51a93fe075f00caec109a80d293edcd18031d85f04eaed4ce9224872f
3
+ metadata.gz: 2265855682bc867f2ea15fc26c9a2329cf651cb8b34b71634aed79de302565db
4
+ data.tar.gz: a9b2a8c2df61b61bc9587834448c001286688462eeaa673d48a40afca992abf0
5
5
  SHA512:
6
- metadata.gz: 37cc79709b04222aa05297f68dfbcde6b1f087027d9e8b7e6fd5c7894b7b6c629a42a4a02f26e8b388fcbedeccf07b23ad3b4bad42290ad8ba4c1d7715f01847
7
- data.tar.gz: 1f71874261f1e5e610314106fe5afb08a34f0612cea7aaae6618476e8b743eab4491f8ee01a3393d3463e794e97dc491e18385c482363270ae6c7791867d8c40
6
+ metadata.gz: 3544b511f83935f75a5ee1e0535f3c2333b914a792fb5c2fe15744f7d5f631d55a8a501a84a4614acf53f676d760e164acd29ed788d85c5b31f8628fc0f4b46c
7
+ data.tar.gz: 7ca97c724f07c1abaab2198a2f6710e8d477d24a13b933896e928eb0d7be2d24b3644062b46a342642a683f646cc990cc3136564d5bbd30093545e62527f9114
data/README.adoc CHANGED
@@ -423,7 +423,7 @@ related_concept.ref = Glossarist::ConceptRef.new(source: "IEC", id: "102-03-02")
423
423
  [[relationship-types]]
424
424
  ==== Relationship Types
425
425
 
426
- Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX. The table below shows each type with its provenance and cross-standard equivalents.
426
+ Relationship types span five standards (ISO 10241-1, ISO 25964/SKOS, ISO 12620/TBX, ISO 19135). The table below shows each of the 52 types with its category and cross-standard equivalents.
427
427
 
428
428
  [cols="1,1,1,1,1"]
429
429
  |===
@@ -435,6 +435,12 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
435
435
  |—
436
436
  |—
437
437
 
438
+ |`deprecated_by`
439
+ |Lifecycle
440
+ |deprecated by
441
+ |—
442
+ |—
443
+
438
444
  |`supersedes`
439
445
  |Lifecycle
440
446
  |supersedes
@@ -447,6 +453,42 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
447
453
  |—
448
454
  |—
449
455
 
456
+ |`replaces`
457
+ |Lifecycle (ISO 19135)
458
+ |—
459
+ |—
460
+ |—
461
+
462
+ |`replaced_by`
463
+ |Lifecycle (ISO 19135)
464
+ |—
465
+ |—
466
+ |—
467
+
468
+ |`invalidates`
469
+ |Lifecycle (ISO 19135)
470
+ |—
471
+ |—
472
+ |—
473
+
474
+ |`invalidated_by`
475
+ |Lifecycle (ISO 19135)
476
+ |—
477
+ |—
478
+ |—
479
+
480
+ |`retires`
481
+ |Lifecycle (ISO 19135)
482
+ |—
483
+ |—
484
+ |—
485
+
486
+ |`retired_by`
487
+ |Lifecycle (ISO 19135)
488
+ |—
489
+ |—
490
+ |—
491
+
450
492
  |`broader`
451
493
  |Hierarchical
452
494
  |broader concept
@@ -483,6 +525,18 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
483
525
  |NTP (narrowerPartitive)
484
526
  |narrowerTermPartitive
485
527
 
528
+ |`has_part`
529
+ |Hierarchical (partitive)
530
+ |has part
531
+ |—
532
+ |—
533
+
534
+ |`is_part_of`
535
+ |Hierarchical (partitive)
536
+ |is part of
537
+ |—
538
+ |—
539
+
486
540
  |`broader_instantial`
487
541
  |Hierarchical (instantial)
488
542
  |—
@@ -495,6 +549,18 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
495
549
  |NTI (narrowerInstantial)
496
550
  |narrowerTermInstantial
497
551
 
552
+ |`instance_of`
553
+ |Hierarchical (instantial)
554
+ |instance of
555
+ |—
556
+ |—
557
+
558
+ |`has_instance`
559
+ |Hierarchical (instantial)
560
+ |has instance
561
+ |—
562
+ |—
563
+
498
564
  |`equivalent`
499
565
  |Equivalence
500
566
  |equivalent
@@ -543,6 +609,12 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
543
609
  |RT (relatedTerm)
544
610
  |crossReference
545
611
 
612
+ |`references`
613
+ |Associative
614
+ |references
615
+ |—
616
+ |—
617
+
546
618
  |`related_concept`
547
619
  |Associative
548
620
  |—
@@ -561,23 +633,23 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
561
633
  |—
562
634
  |relatedConceptNarrower
563
635
 
564
- |`sequentially_related_concept`
636
+ |`sequentially_related`
565
637
  |Associative (sequential)
566
638
  |—
567
639
  |—
568
- |sequentiallyRelatedConcept
640
+ |sequentiallyRelated
569
641
 
570
- |`spatially_related_concept`
642
+ |`spatially_related`
571
643
  |Associative (spatial)
572
644
  |—
573
645
  |—
574
- |spatiallyRelatedConcept
646
+ |spatiallyRelated
575
647
 
576
- |`temporally_related_concept`
648
+ |`temporally_related`
577
649
  |Associative (temporal)
578
650
  |—
579
651
  |—
580
- |temporallyRelatedConcept
652
+ |temporallyRelated
581
653
 
582
654
  |`homograph`
583
655
  |Lexical
@@ -590,6 +662,66 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
590
662
  |—
591
663
  |—
592
664
  |falseFriend
665
+
666
+ |`has_concept`
667
+ |Register (ISO 19135)
668
+ |—
669
+ |—
670
+ |—
671
+
672
+ |`is_concept_of`
673
+ |Register (ISO 19135)
674
+ |—
675
+ |—
676
+ |—
677
+
678
+ |`has_definition`
679
+ |Register (ISO 19135)
680
+ |—
681
+ |—
682
+ |—
683
+
684
+ |`definition_of`
685
+ |Register (ISO 19135)
686
+ |—
687
+ |—
688
+ |—
689
+
690
+ |`inherits`
691
+ |Register (ISO 19135)
692
+ |—
693
+ |—
694
+ |—
695
+
696
+ |`inherited_by`
697
+ |Register (ISO 19135)
698
+ |—
699
+ |—
700
+ |—
701
+
702
+ |`has_version`
703
+ |Versioning (ISO 19135)
704
+ |—
705
+ |—
706
+ |—
707
+
708
+ |`version_of`
709
+ |Versioning (ISO 19135)
710
+ |—
711
+ |—
712
+ |—
713
+
714
+ |`current_version`
715
+ |Versioning (ISO 19135)
716
+ |—
717
+ |—
718
+ |—
719
+
720
+ |`current_version_of`
721
+ |Versioning (ISO 19135)
722
+ |—
723
+ |—
724
+ |—
593
725
  |===
594
726
 
595
727
  [[id,concept-reference]]
@@ -598,12 +730,12 @@ Relationship types are drawn from ISO 10241-1, ISO 25964/SKOS, and ISO 12620/TBX
598
730
  A typed reference to another concept, either local (within the same glossary) or external (in another concept registry).
599
731
 
600
732
  term:: String — the display text for the referenced concept.
601
- concept_id:: String — the identifier of the target concept.
733
+ concept_id:: String — the identifier of the target concept. Also accepts the `id` key in YAML (alias for backward compatibility with the concept-model format).
602
734
  source:: String — the registry URI prefix for external references (e.g. `urn:iec:std:iec:60050`).
603
- ref_type:: String — the reference type: `local`, `designation`, or `urn`.
735
+ ref_type:: String — the reference type: `local`, `designation`, `cite`, `section`, `domain`, or `urn`.
604
736
  urn:: String — a direct URN for the target concept (e.g. `urn:iec:std:iec:60050-102-01-01`).
605
737
 
606
- Local references use `concept_id` without `source`. External references use `source` + `concept_id` or a direct `urn`.
738
+ Local references use `concept_id` without `source`. External references use `source` + `concept_id` or a direct `urn`. `cite` references resolve against a `ConceptSource#id` in the same concept. `section` references point to a section defined in `register.yaml`.
607
739
 
608
740
  [,ruby]
609
741
  ----
@@ -812,6 +944,58 @@ origin:: The bibliographic <<citation,citation>> for the managed term.
812
944
  modification:: A description of the modification to the cited definition of the term, if any, as it is to be applied in the present context.
813
945
 
814
946
 
947
+ [[dataset-register,Dataset Register]]
948
+ == Dataset Register
949
+
950
+ A dataset directory may contain a `register.yaml` — the self-describing metadata file. It carries identity (URN, ref, year), structure (ordering, hierarchical sections), provenance (owner, sourceRepo), and relationships (supersedes other datasets).
951
+
952
+ [,yaml]
953
+ ----
954
+ schema_type: glossarist
955
+ schema_version: "3"
956
+ id: iso-19111
957
+ ref: "ISO 19111:2019"
958
+ year: 2019
959
+ urn: "urn:iso:std:iso:19111"
960
+ status: current
961
+ owner: ISO
962
+ languages: [eng, fra]
963
+ ordering: systematic
964
+ sections:
965
+ - id: "3"
966
+ names: { eng: "Geometric concepts" }
967
+ children:
968
+ - id: "3.1"
969
+ names: { eng: "Points and lines" }
970
+ ----
971
+
972
+ [[hierarchical-sections,Hierarchical Sections]]
973
+ === Hierarchical sections
974
+
975
+ Sections provide structural organization for concepts within a dataset. A section may contain child sections, forming a tree. Concepts reference sections via `domains[]` with `ref_type: section`:
976
+
977
+ [,yaml]
978
+ ----
979
+ data:
980
+ domains:
981
+ - source: urn:iso:std:iso:19111
982
+ id: "3.1"
983
+ ref_type: section
984
+ ----
985
+
986
+ ==== Cascading membership
987
+
988
+ Section membership is transitive: a concept in section `3.1.1` is also a member of `3.1` and `3` for filtering and display. Use `DatasetRegister#concept_section_ids(concept)` to resolve all sections a concept belongs to, including transitive ancestors.
989
+
990
+ [,ruby]
991
+ ----
992
+ register = Glossarist::DatasetRegister.from_file("path/to/register.yaml")
993
+ register.concept_section_ids(concept) # => ["3.1", "3"]
994
+ ----
995
+
996
+ When a concept has no explicit `domains[]` entry with `ref_type: section`, section membership is derived from the concept's identifier using the longest registered section prefix (e.g. `103-01-01` → section `103`).
997
+
998
+
815
999
  == Commands
816
1000
 
817
1001
  === generate_latex
@@ -1351,6 +1535,15 @@ Upgrade a dataset to the current schema version.
1351
1535
  glossarist upgrade SOURCE_DIR -o OUTPUT_DIR
1352
1536
  ----
1353
1537
 
1538
+ === version
1539
+
1540
+ Show the installed Glossarist version.
1541
+
1542
+ [,bash]
1543
+ ----
1544
+ glossarist version
1545
+ ----
1546
+
1354
1547
  == Glossarist Concept Repository (GCR)
1355
1548
 
1356
1549
  A **GCR** (Glossarist Concept Repository) is a distributable, versioned ZIP archive containing glossary concepts and metadata. GCR packages are created from v2 datasets.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Collections
5
+ class FigureCollection < NonVerbalCollection
6
+ instances :entries, Figure
7
+
8
+ key_value { map_instances to: :entries }
9
+
10
+ def self.from_directory(dir)
11
+ collection = new
12
+ Dir.glob(File.join(dir, "*.yaml")).each do |path|
13
+ fig = Figure.from_file(path)
14
+ collection.store(fig) if fig
15
+ end
16
+ collection
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Collections
5
+ class FormulaCollection < NonVerbalCollection
6
+ instances :entries, Formula
7
+
8
+ key_value { map_instances to: :entries }
9
+
10
+ def self.from_directory(dir)
11
+ collection = new
12
+ Dir.glob(File.join(dir, "*.yaml")).each do |path|
13
+ fml = Formula.from_file(path)
14
+ collection.store(fml) if fml
15
+ end
16
+ collection
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Collections
5
+ # Generic collection for dataset-level non-verbal entities
6
+ # (Figure, Table, Formula). Provides by_id lookup across top-level
7
+ # entities and recursive subfigures (for Figure).
8
+ #
9
+ # Subclasses declare the entity type via `instances`.
10
+ class NonVerbalCollection < Lutaml::Model::Collection
11
+ # Find an entity by ID, searching recursively (Figure subfigures).
12
+ #
13
+ # @param id [String] the entity or sub-entity ID
14
+ # @return [NonVerbalEntity, nil]
15
+ def by_id(target_id)
16
+ entries.each do |entity|
17
+ found = entity.find_by_id(target_id)
18
+ return found if found
19
+ end
20
+ nil
21
+ end
22
+
23
+ # All entity IDs including sub-entity IDs (for Figure subfigures).
24
+ #
25
+ # @return [Set<String>]
26
+ def ids
27
+ Set.new(entries.flat_map(&:all_ids).compact)
28
+ end
29
+
30
+ # Check if an entity with the given ID exists.
31
+ #
32
+ # @param id [String]
33
+ # @return [Boolean]
34
+ def exists?(id)
35
+ !by_id(id).nil?
36
+ end
37
+
38
+ # Store an entity.
39
+ #
40
+ # @param entity [NonVerbalEntity]
41
+ def store(entity)
42
+ entries << entity
43
+ end
44
+ alias :<< :store
45
+
46
+ def entries
47
+ @entries ||= []
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Collections
5
+ class TableCollection < NonVerbalCollection
6
+ instances :entries, Table
7
+
8
+ key_value { map_instances to: :entries }
9
+
10
+ def self.from_directory(dir)
11
+ collection = new
12
+ Dir.glob(File.join(dir, "*.yaml")).each do |path|
13
+ tbl = Table.from_file(path)
14
+ collection.store(tbl) if tbl
15
+ end
16
+ collection
17
+ end
18
+ end
19
+ end
20
+ end
@@ -15,6 +15,14 @@ module Glossarist
15
15
  "glossarist/collections/detailed_definition_collection"
16
16
  autoload :LocalizationCollection,
17
17
  "glossarist/collections/localization_collection"
18
+ autoload :NonVerbalCollection,
19
+ "glossarist/collections/non_verbal_collection"
20
+ autoload :FigureCollection,
21
+ "glossarist/collections/figure_collection"
22
+ autoload :TableCollection,
23
+ "glossarist/collections/table_collection"
24
+ autoload :FormulaCollection,
25
+ "glossarist/collections/formula_collection"
18
26
  autoload :TypedCollection,
19
27
  "glossarist/collections/typed_collection"
20
28
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A dataset-level figure entity (ISO 10241-1 §6.5 — non-verbal representation).
5
+ #
6
+ # Figures are authored once at `datasets/{ds}/figures/{fig-id}.yaml` and
7
+ # referenced by any number of concepts via stable ID — the same sharing
8
+ # pattern as bibliography entries. This is the rich, shareable counterpart
9
+ # to concept-owned NonVerbRep entries.
10
+ #
11
+ # A Figure may carry multiple image variants (SVG + PNG + dark/light) for
12
+ # responsive rendering and accessibility. Composite figures use recursive
13
+ # subfigures.
14
+ #
15
+ # Caption, description, and alt are localized (hash keyed by ISO 639 code).
16
+ class Figure < NonVerbalEntity
17
+ attribute :images, FigureImage, collection: true
18
+ attribute :subfigures, Figure, collection: true
19
+
20
+ key_value do
21
+ map :id, to: :id
22
+ map :identifier, to: :identifier
23
+ map :caption, to: :caption
24
+ map :description, to: :description
25
+ map :alt, to: :alt
26
+ map :images, to: :images
27
+ map :sources, to: :sources
28
+ map :subfigures, to: :subfigures
29
+ end
30
+
31
+ # Recursively search for a subfigure (or self) by ID.
32
+ #
33
+ # @param target_id [String] the figure or subfigure ID
34
+ # @return [Figure, nil]
35
+ def find_by_id(target_id)
36
+ return self if id == target_id
37
+
38
+ Array(subfigures).each do |sub|
39
+ found = sub.find_by_id(target_id)
40
+ return found if found
41
+ end
42
+ nil
43
+ end
44
+
45
+ # Collect this figure's ID and all descendant subfigure IDs.
46
+ #
47
+ # @return [Array<String>]
48
+ def all_ids
49
+ [id] + Array(subfigures).flat_map(&:all_ids)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # One image variant within a Figure. Multiple variants enable responsive
5
+ # images, format fallbacks, and accessibility (dark/light, language-specific).
6
+ #
7
+ # The `role` field drives consumer-side selection:
8
+ # vector — SVG (preferred for diagrams, resolution-independent)
9
+ # raster — PNG/JPG (preferred for photos)
10
+ # dark — optimized for dark backgrounds
11
+ # light — optimized for light backgrounds
12
+ # print — high-resolution for print output
13
+ class FigureImage < Lutaml::Model::Serializable
14
+ attribute :src, :string
15
+ attribute :format, :string
16
+ attribute :role, :string
17
+ attribute :width, :integer
18
+ attribute :height, :integer
19
+ attribute :scale, :integer
20
+
21
+ key_value do
22
+ map :src, to: :src
23
+ map :format, to: :format
24
+ map :role, to: :role
25
+ map :width, to: :width
26
+ map :height, to: :height
27
+ map :scale, to: :scale
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A reference from a concept to a dataset-level Figure entity.
5
+ #
6
+ # Produced by `figures: [id]` structural arrays and `{{fig:id}}` inline
7
+ # mentions. Both forms resolve through the same figure adapter.
8
+ class FigureReference < NonVerbalReference
9
+ key_value do
10
+ map :entity_id, to: :entity_id
11
+ map :display, to: :display
12
+ end
13
+
14
+ def dedup_key
15
+ [self.class.name, entity_id]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A dataset-level formula entity (ISO 10241-1 §6.5 — non-verbal representation).
5
+ #
6
+ # Formulas are authored at `datasets/{ds}/formulas/{formula-id}.yaml` and
7
+ # shared across concepts. The mathematical expression is stored in a
8
+ # notation format (LaTeX, MathML, AsciiMath). Caption, description, and
9
+ # alt are localized for accessibility.
10
+ class Formula < NonVerbalEntity
11
+ attribute :expression, :hash
12
+ attribute :notation, :string
13
+
14
+ key_value do
15
+ map :id, to: :id
16
+ map :identifier, to: :identifier
17
+ map :caption, to: :caption
18
+ map :description, to: :description
19
+ map :alt, to: :alt
20
+ map :expression, to: :expression
21
+ map :notation, to: :notation
22
+ map :sources, to: :sources
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A reference from a concept to a dataset-level Formula entity.
5
+ #
6
+ # Produced by `formulas: [id]` structural arrays and `{{formula:id}}` inline
7
+ # mentions.
8
+ class FormulaReference < NonVerbalReference
9
+ key_value do
10
+ map :entity_id, to: :entity_id
11
+ map :display, to: :display
12
+ end
13
+
14
+ def dedup_key
15
+ [self.class.name, entity_id]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # Semantic operations on localized string fields.
5
+ #
6
+ # Localized strings are stored as hashes keyed by ISO 639 language code:
7
+ # { "eng" => "Mixed reflection", "fra" => "Réflexion mixte" }
8
+ #
9
+ # This module provides typed access and fallback logic. It does not
10
+ # introduce a wrapper class — the hash IS the localized string, matching
11
+ # how Section#names, DatasetRegister#description, etc. already work.
12
+ module LocalizedString
13
+ # Fetch a localized value with language fallback.
14
+ #
15
+ # @param hash [Hash, nil] the localized string hash
16
+ # @param lang [String, Symbol] the desired language code
17
+ # @param fallback [String, nil] fallback language (default "eng")
18
+ # @return [String, nil] the localized value, or nil if not found
19
+ def self.fetch(hash, lang, fallback = "eng")
20
+ return nil unless hash.is_a?(Hash)
21
+
22
+ direct = hash[lang.to_s] || hash[lang.to_sym]
23
+ return direct if direct
24
+
25
+ fallback && fallback.to_s != lang.to_s ? hash[fallback.to_s] || hash[fallback.to_sym] : nil
26
+ end
27
+
28
+ # Check if a localized string hash is nil or empty.
29
+ #
30
+ # @param hash [Hash, nil]
31
+ # @return [Boolean]
32
+ def self.empty?(hash)
33
+ hash.nil? || hash.empty?
34
+ end
35
+
36
+ # Check if a localized string hash has any entries.
37
+ #
38
+ # @param hash [Hash, nil]
39
+ # @return [Boolean]
40
+ def self.present?(hash)
41
+ !empty?(hash)
42
+ end
43
+ end
44
+ end
@@ -7,6 +7,9 @@ module Glossarist
7
7
  attribute :localized_concepts, :hash
8
8
  attribute :domains, ConceptReference, collection: true
9
9
  attribute :tags, :string, collection: true
10
+ attribute :figures, FigureReference, collection: true
11
+ attribute :tables, TableReference, collection: true
12
+ attribute :formulas, FormulaReference, collection: true
10
13
  attribute :sources, ConceptSource, collection: true
11
14
  attribute :localizations, LocalizedConcept,
12
15
  collection: Collections::LocalizationCollection,
@@ -21,6 +24,12 @@ module Glossarist
21
24
  map %i[domains groups], to: :domains,
22
25
  with: { from: :domains_from_yaml, to: :domains_to_yaml }
23
26
  map :tags, to: :tags
27
+ map :figures, to: :figures,
28
+ with: { from: :figures_from_yaml, to: :figures_to_yaml }
29
+ map :tables, to: :tables,
30
+ with: { from: :tables_from_yaml, to: :tables_to_yaml }
31
+ map :formulas, to: :formulas,
32
+ with: { from: :formulas_from_yaml, to: :formulas_to_yaml }
24
33
  map :sources, to: :sources
25
34
  map :localizations, to: :localizations,
26
35
  with: { from: :localizations_from_yaml, to: :localizations_to_yaml }
@@ -63,6 +72,46 @@ module Glossarist
63
72
  doc["domains"] = model.domains.map(&:to_hash)
64
73
  end
65
74
 
75
+ def figures_from_yaml(model, value)
76
+ model.figures = parse_non_verbal_refs(value, FigureReference)
77
+ end
78
+
79
+ def figures_to_yaml(model, doc)
80
+ serialize_non_verbal_refs(model.figures, doc, "figures")
81
+ end
82
+
83
+ def tables_from_yaml(model, value)
84
+ model.tables = parse_non_verbal_refs(value, TableReference)
85
+ end
86
+
87
+ def tables_to_yaml(model, doc)
88
+ serialize_non_verbal_refs(model.tables, doc, "tables")
89
+ end
90
+
91
+ def formulas_from_yaml(model, value)
92
+ model.formulas = parse_non_verbal_refs(value, FormulaReference)
93
+ end
94
+
95
+ def formulas_to_yaml(model, doc)
96
+ serialize_non_verbal_refs(model.formulas, doc, "formulas")
97
+ end
98
+
99
+ private
100
+
101
+ def parse_non_verbal_refs(value, ref_class)
102
+ return unless value.is_a?(Array)
103
+
104
+ value.map { |item| ref_class.of_yaml(item) }
105
+ end
106
+
107
+ def serialize_non_verbal_refs(refs, doc, key)
108
+ return if refs.nil? || refs.empty?
109
+
110
+ doc[key] = refs.map do |ref|
111
+ ref.display ? { "ref" => ref.entity_id, "display" => ref.display } : ref.entity_id
112
+ end
113
+ end
114
+
66
115
  def authoritative_source
67
116
  return [] unless sources
68
117
 
@@ -13,17 +13,23 @@ module Glossarist
13
13
  # - +type+: one of "image", "table", "formula"
14
14
  # - +ref+: URI reference to the resource (relative path, URN, or URL)
15
15
  # - +text+: optional text description or alt text
16
+ # - +caption+: localized title/caption (accessibility)
17
+ # - +description+: localized long description (accessibility)
16
18
  # - +sources+: bibliographic sources for the representation
17
19
  class NonVerbRep < Lutaml::Model::Serializable
18
20
  attribute :type, :string
19
21
  attribute :ref, :string
20
22
  attribute :text, :string
23
+ attribute :caption, :hash
24
+ attribute :description, :hash
21
25
  attribute :sources, ConceptSource, collection: true
22
26
 
23
27
  key_value do
24
28
  map :type, to: :type
25
29
  map :ref, to: :ref
26
30
  map :text, to: :text
31
+ map :caption, to: :caption
32
+ map :description, to: :description
27
33
  map :sources, to: :sources
28
34
  end
29
35
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # Abstract base for dataset-level non-verbal representation entities.
5
+ #
6
+ # Figures, Tables, and Formulas share common metadata: stable identity,
7
+ # localized caption/description (accessibility), and provenance sources.
8
+ # Each is authored once at the dataset level and referenced by any number
9
+ # of concepts — the same pattern as bibliography entries.
10
+ #
11
+ # This is the dataset-level counterpart to NonVerbRep (ISO 10241-1 §6.5),
12
+ # which remains the concept-owned inline form.
13
+ #
14
+ # Subclasses (Figure, Table, Formula) add type-specific content fields.
15
+ class NonVerbalEntity < Lutaml::Model::Serializable
16
+ attribute :id, :string
17
+ attribute :identifier, :string
18
+ attribute :caption, :hash
19
+ attribute :description, :hash
20
+ attribute :alt, :hash
21
+ attribute :sources, ConceptSource, collection: true
22
+
23
+ key_value do
24
+ map :id, to: :id
25
+ map :identifier, to: :identifier
26
+ map :caption, to: :caption
27
+ map :description, to: :description
28
+ map :alt, to: :alt
29
+ map :sources, to: :sources
30
+ end
31
+
32
+ # Find self by ID. Figure overrides for recursive subfigure search.
33
+ #
34
+ # @param target_id [String]
35
+ # @return [NonVerbalEntity, nil]
36
+ def find_by_id(target_id)
37
+ id == target_id ? self : nil
38
+ end
39
+
40
+ # This entity's IDs. Figure overrides to include subfigure IDs.
41
+ #
42
+ # @return [Array<String>]
43
+ def all_ids
44
+ [id]
45
+ end
46
+
47
+ def self.from_file(path)
48
+ return nil unless File.exist?(path)
49
+
50
+ from_yaml(File.read(path, encoding: "utf-8"))
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # Abstract base for references to dataset-level non-verbal entities.
5
+ #
6
+ # FigureReference, TableReference, and FormulaReference all carry an
7
+ # entity ID and an optional display override. They are produced both by
8
+ # structural arrays (`figures: [id]` on ManagedConceptData) and by inline
9
+ # mentions (`{{fig:id}}` in text).
10
+ class NonVerbalReference < Lutaml::Model::Serializable
11
+ attribute :entity_id, :string
12
+ attribute :display, :string
13
+
14
+ def self.of_yaml(hash)
15
+ return new(entity_id: hash) if hash.is_a?(String)
16
+
17
+ new(
18
+ entity_id: hash["ref"] || hash["id"] || hash[:ref] || hash[:id],
19
+ display: hash["display"] || hash[:display],
20
+ )
21
+ end
22
+ end
23
+ end
@@ -226,6 +226,15 @@ module Glossarist
226
226
  AssetReference.new(path: path.strip)
227
227
  end
228
228
 
229
+ # Unified non-verbal entity mention resolver for fig:/table:/formula:.
230
+ # Strips the prefix and produces the appropriate reference type.
231
+ def resolve_non_verbal_mention(prefix, identifier, display, ref_class)
232
+ cleaned = identifier.delete_prefix(prefix).strip
233
+ return nil if cleaned.empty?
234
+
235
+ ref_class.new(entity_id: cleaned, display: display)
236
+ end
237
+
229
238
  private
230
239
 
231
240
  def gather_texts(lc_hash)
@@ -293,6 +302,18 @@ module Glossarist
293
302
  ext.resolve_cite_key(identifier, display)
294
303
  end
295
304
 
305
+ register_identifier_resolver("fig:") do |ext, identifier, display|
306
+ ext.resolve_non_verbal_mention("fig:", identifier, display, FigureReference)
307
+ end
308
+
309
+ register_identifier_resolver("table:") do |ext, identifier, display|
310
+ ext.resolve_non_verbal_mention("table:", identifier, display, TableReference)
311
+ end
312
+
313
+ register_identifier_resolver("formula:") do |ext, identifier, display|
314
+ ext.resolve_non_verbal_mention("formula:", identifier, display, FormulaReference)
315
+ end
316
+
296
317
  register_identifier_resolver("urn:iec:std:iec:60050") do |ext, identifier, display|
297
318
  ext.resolve_iec_urn(identifier, display)
298
319
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A dataset-level table entity (ISO 10241-1 §6.5 — non-verbal representation).
5
+ #
6
+ # Tables are authored at `datasets/{ds}/tables/{table-id}.yaml` and shared
7
+ # across concepts. The content is stored as structured data (rows/columns)
8
+ # or as a markup string (HTML, Markdown, AsciiDoc). Caption, description,
9
+ # and alt are localized for accessibility.
10
+ class Table < NonVerbalEntity
11
+ attribute :content, :hash
12
+ attribute :format, :string
13
+
14
+ key_value do
15
+ map :id, to: :id
16
+ map :identifier, to: :identifier
17
+ map :caption, to: :caption
18
+ map :description, to: :description
19
+ map :alt, to: :alt
20
+ map :content, to: :content
21
+ map :format, to: :format
22
+ map :sources, to: :sources
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # A reference from a concept to a dataset-level Table entity.
5
+ #
6
+ # Produced by `tables: [id]` structural arrays and `{{table:id}}` inline
7
+ # mentions.
8
+ class TableReference < NonVerbalReference
9
+ key_value do
10
+ map :entity_id, to: :entity_id
11
+ map :display, to: :display
12
+ end
13
+
14
+ def dedup_key
15
+ [self.class.name, entity_id]
16
+ end
17
+ end
18
+ end
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- VERSION = "2.8.14"
7
+ VERSION = "2.8.15"
8
8
  end
data/lib/glossarist.rb CHANGED
@@ -38,6 +38,16 @@ module Glossarist
38
38
  autoload :ConceptDocument, "glossarist/concept_document"
39
39
  autoload :ConceptEnricher, "glossarist/concept_enricher"
40
40
  autoload :Config, "glossarist/config"
41
+ autoload :LocalizedString, "glossarist/localized_string"
42
+ autoload :NonVerbalEntity, "glossarist/non_verbal_entity"
43
+ autoload :NonVerbalReference, "glossarist/non_verbal_reference"
44
+ autoload :Figure, "glossarist/figure"
45
+ autoload :FigureImage, "glossarist/figure_image"
46
+ autoload :FigureReference, "glossarist/figure_reference"
47
+ autoload :Table, "glossarist/table"
48
+ autoload :TableReference, "glossarist/table_reference"
49
+ autoload :Formula, "glossarist/formula"
50
+ autoload :FormulaReference, "glossarist/formula_reference"
41
51
  autoload :DatasetValidator, "glossarist/dataset_validator"
42
52
  autoload :CustomLocality, "glossarist/custom_locality"
43
53
  autoload :DetailedDefinition, "glossarist/detailed_definition"
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.14
4
+ version: 2.8.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-16 00:00:00.000000000 Z
11
+ date: 2026-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -195,7 +195,11 @@ files:
195
195
  - lib/glossarist/collections/concept_source_collection.rb
196
196
  - lib/glossarist/collections/designation_collection.rb
197
197
  - lib/glossarist/collections/detailed_definition_collection.rb
198
+ - lib/glossarist/collections/figure_collection.rb
199
+ - lib/glossarist/collections/formula_collection.rb
198
200
  - lib/glossarist/collections/localization_collection.rb
201
+ - lib/glossarist/collections/non_verbal_collection.rb
202
+ - lib/glossarist/collections/table_collection.rb
199
203
  - lib/glossarist/collections/typed_collection.rb
200
204
  - lib/glossarist/comparison_result.rb
201
205
  - lib/glossarist/concept.rb
@@ -235,6 +239,11 @@ files:
235
239
  - lib/glossarist/errors/invalid_type_error.rb
236
240
  - lib/glossarist/errors/load_error.rb
237
241
  - lib/glossarist/errors/parse_error.rb
242
+ - lib/glossarist/figure.rb
243
+ - lib/glossarist/figure_image.rb
244
+ - lib/glossarist/figure_reference.rb
245
+ - lib/glossarist/formula.rb
246
+ - lib/glossarist/formula_reference.rb
238
247
  - lib/glossarist/gcr_metadata.rb
239
248
  - lib/glossarist/gcr_package.rb
240
249
  - lib/glossarist/gcr_package_definition.rb
@@ -244,10 +253,13 @@ files:
244
253
  - lib/glossarist/glossary_store.rb
245
254
  - lib/glossarist/locality.rb
246
255
  - lib/glossarist/localized_concept.rb
256
+ - lib/glossarist/localized_string.rb
247
257
  - lib/glossarist/managed_concept.rb
248
258
  - lib/glossarist/managed_concept_collection.rb
249
259
  - lib/glossarist/managed_concept_data.rb
250
260
  - lib/glossarist/non_verb_rep.rb
261
+ - lib/glossarist/non_verbal_entity.rb
262
+ - lib/glossarist/non_verbal_reference.rb
251
263
  - lib/glossarist/pronunciation.rb
252
264
  - lib/glossarist/rdf.rb
253
265
  - lib/glossarist/rdf/gloss_citation.rb
@@ -298,6 +310,8 @@ files:
298
310
  - lib/glossarist/sts/importer.rb
299
311
  - lib/glossarist/sts/term_extractor.rb
300
312
  - lib/glossarist/sts/term_mapper.rb
313
+ - lib/glossarist/table.rb
314
+ - lib/glossarist/table_reference.rb
301
315
  - lib/glossarist/transforms.rb
302
316
  - lib/glossarist/transforms/concept_to_gloss_transform.rb
303
317
  - lib/glossarist/transforms/concept_to_tbx_transform.rb