glossarist 2.13.10 → 2.13.12

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: 16c44282632761dbbfeb3b63ff619b519648f0af7306ee5aa85fba1606244bd6
4
- data.tar.gz: 37e8af5033c014195228ba11f50528b7aca8c06424533196ea0c318d5f78c7f0
3
+ metadata.gz: 5b15e9fa1ab722c42698d7b4f87a9517557aaeffbf44ed56907d20db28a678d3
4
+ data.tar.gz: 92743d7653a3a64f09da6098aea9308ca809db2a2ac4108766590cf5dbea791c
5
5
  SHA512:
6
- metadata.gz: 8dadb718f3a39e49167eb1d8aa8eeada22f826c049be68dcfe151cd0980edb0e80006a51b21f64912dcc9e9d4ef21ff47618002b6d7d1496b88669cb39932620
7
- data.tar.gz: 29f66da8f1f743c7f5424e966bf4b19a94e83264146d9c3156243f0f0a39e728874fcc4cbf6f2a2d8c77bd4799406083162d27b5f675dd1fe0aa88246091222d
6
+ metadata.gz: '02780e9a932c36e5d769d8645b37a36c372fabaa3acee9cc85760d5b6734013b524d0a41d06c9dbb06ace71aba2186797dd022f9f055fc761ffa5c605a42a7bb'
7
+ data.tar.gz: b41226d8877739287fafa314e1fd7b5be6574475e839dcff4a218f8a003a618549e0b01a89b7bc38a76d3a708390bd6b7a8f8d2f15afea62b181b41add392fc5
@@ -4,39 +4,44 @@ module Glossarist
4
4
  # A typed reference to another concept, used inside n-ary relation
5
5
  # members and other internal pointer shapes.
6
6
  #
7
- # The base ConceptRef carries `source`, `id`, and `text`. The richer
8
- # `ConceptReference` (in `glossarist/concept_reference.rb`) extends
9
- # this with `term`, `urn`, `version`, `ref_type` for use in
10
- # cross-vocabulary references and relevance contexts.
7
+ # The base ConceptRef carries `source`, `id`, `text`, `external`, and
8
+ # `ellipsis`. The richer `ConceptReference` (in
9
+ # `glossarist/concept_reference.rb`) extends this with `term`, `urn`,
10
+ # `version`, `ref_type` for use in cross-vocabulary references and
11
+ # relevance contexts.
11
12
  #
12
13
  # A ConceptRef is identified by `source:id` for external references
13
14
  # or just `id` for local references. The `qualified_id` class method
14
15
  # is the single SSOT for this string format — used by RelationLoader,
15
16
  # the RDF transform, and ConceptRef-bearing data structures.
17
+ #
18
+ # == External concepts (parenthetical notation)
19
+ #
20
+ # ISO 704:2022 uses parenthetical notation for external concepts —
21
+ # concepts referenced from this dataset but defined elsewhere. The
22
+ # `external: true` flag marks such a ref. External refs carry `text`
23
+ # (the parenthetical label) but NOT `source`/`id`.
24
+ #
25
+ # == Ellipsis (structural marker)
26
+ #
27
+ # An ellipsis ref marks "further members exist but are not encoded."
28
+ # It is NOT a concept — it's a structural marker on the hyperedge.
29
+ # An ellipsis ref carries NO other fields (no source, id, text).
16
30
  class ConceptRef < Lutaml::Model::Serializable
17
31
  attribute :source, :string
18
32
  attribute :id, :string
19
33
  attribute :text, :string
34
+ attribute :external, :boolean, default: -> { false }
35
+ attribute :ellipsis, :boolean, default: -> { false }
20
36
 
21
37
  key_value do
22
38
  map :source, to: :source
23
39
  map :id, to: :id
24
40
  map :text, to: :text
41
+ map :external, to: :external
42
+ map :ellipsis, to: :ellipsis
25
43
  end
26
44
 
27
- # SSOT for the canonical "qualified identifier" string of a
28
- # ConceptRef. Returns:
29
- #
30
- # - "{source}:{id}" when both source and id are present
31
- # - "{id}" when only id is present (local ref)
32
- # - "{source}:{text}" when source and text are present (no id)
33
- # - "{text}" when only text is present (external
34
- # concept form, no resolvable id)
35
- # - nil when the ref is empty / not a ConceptRef
36
- #
37
- # Two refs are considered to point at the same concept iff their
38
- # qualified_id is equal. Tables and registries can use this
39
- # directly as a hash key.
40
45
  def self.qualified_id(ref)
41
46
  return nil unless ref.is_a?(ConceptRef)
42
47
 
@@ -51,14 +56,50 @@ module Glossarist
51
56
  end
52
57
  end
53
58
 
54
- # Per-instance convenience delegating to the class method.
55
59
  def qualified_id
56
60
  self.class.qualified_id(self)
57
61
  end
58
62
 
59
- # True iff +other+ refers to the same concept as self.
60
63
  def same_concept?(other)
61
64
  qualified_id == self.class.qualified_id(other)
62
65
  end
66
+
67
+ def resolved?
68
+ !source.to_s.empty? && !id.to_s.empty?
69
+ end
70
+
71
+ def external?
72
+ external == true
73
+ end
74
+
75
+ def ellipsis?
76
+ ellipsis == true
77
+ end
78
+
79
+ def text_only?
80
+ !text.to_s.empty? && !resolved? && !external?
81
+ end
82
+
83
+ def validate_ref!
84
+ if ellipsis? && (source || id || text || external?)
85
+ raise ArgumentError,
86
+ "ConceptRef with ellipsis: true must not carry any " \
87
+ "other field (source, id, text, external)"
88
+ end
89
+
90
+ if external? && (source || id)
91
+ raise ArgumentError,
92
+ "ConceptRef with external: true must not carry " \
93
+ "source/id — use text for the parenthetical label"
94
+ end
95
+
96
+ if !source && !id && !text && !external? && !ellipsis?
97
+ raise ArgumentError,
98
+ "ConceptRef must have at least one of: source+id, text, " \
99
+ "external, or ellipsis"
100
+ end
101
+
102
+ self
103
+ end
63
104
  end
64
105
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Mentions
5
+ class InvalidMentionError < StandardError
6
+ attr_reader :raw, :reason, :position
7
+
8
+ def initialize(raw:, reason:, position: 0)
9
+ @raw = raw
10
+ @reason = reason
11
+ @position = position
12
+ super("Invalid mention at position #{position}: #{reason}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Mentions
5
+ # Scans a string for {{kind:target}} patterns and returns a mixed
6
+ # array of text segments and parsed mention objects.
7
+ #
8
+ # Invalid mentions raise InvalidMentionError — they are never
9
+ # silently passed through.
10
+ #
11
+ # @see concept-model docs/design/inline-mentions.md
12
+ module Parser
13
+ MENTION_REGEX = /\{\{([^}]+)\}\}/.freeze
14
+
15
+ KINDS = %w[concept cite fig table formula bib link image].freeze
16
+
17
+ module_function
18
+
19
+ # @param text [String] the source text to scan
20
+ # @return [Array<Hash>] segments — text and mention objects
21
+ # @raise [InvalidMentionError] on invalid mention syntax
22
+ def parse(text)
23
+ return [{ kind: "text", content: "" }] unless text.is_a?(String) && !text.empty?
24
+
25
+ segments = []
26
+ pos = 0
27
+
28
+ text.to_enum(:scan, MENTION_REGEX).each do |match|
29
+ raw_content = match[0]
30
+ match_start = Regexp.last_match.offset(0)[0]
31
+
32
+ if match_start > pos
33
+ segments << { kind: "text", content: text[pos...match_start] }
34
+ end
35
+
36
+ segment = parse_mention_content(
37
+ raw_content,
38
+ raw: "{{#{raw_content}}}",
39
+ position: match_start,
40
+ )
41
+ segments << segment
42
+ pos = Regexp.last_match.offset(0)[1]
43
+ end
44
+
45
+ segments << { kind: "text", content: text[pos..] } if pos < text.length
46
+ segments
47
+ end
48
+
49
+ def parse_mention_content(content, raw:, position:)
50
+ content = content.strip
51
+
52
+ unless content =~ /\A(\w+):(.*)\z/m
53
+ raise InvalidMentionError.new(
54
+ raw: raw,
55
+ position: position,
56
+ reason: "Missing kind prefix; use {{concept:DATASET:ID}} or " \
57
+ "{{cite:DATASET:ID}} — got #{raw}",
58
+ )
59
+ end
60
+
61
+ kind = Regexp.last_match(1).downcase
62
+ rest = Regexp.last_match(2).strip
63
+
64
+ unless KINDS.include?(kind)
65
+ raise InvalidMentionError.new(
66
+ raw: raw,
67
+ position: position,
68
+ reason: "Unknown kind '#{kind}'; valid kinds: #{KINDS.join(', ')}",
69
+ )
70
+ end
71
+
72
+ target_str, label = split_target_and_label(rest)
73
+
74
+ target = parse_target(target_str, kind: kind, raw: raw, position: position)
75
+
76
+ validate_target!(kind, target, raw: raw, position: position)
77
+
78
+ {
79
+ kind: kind,
80
+ target: target,
81
+ label: strip_label(label),
82
+ raw: raw,
83
+ start: position,
84
+ end: position + raw.length,
85
+ }
86
+ end
87
+
88
+ def split_target_and_label(rest)
89
+ return [rest, nil] unless rest.include?(",")
90
+
91
+ parts = rest.split(",", 2)
92
+ [parts[0].strip, parts[1].to_s.strip]
93
+ end
94
+ private_class_method :split_target_and_label
95
+
96
+ def parse_target(target_str, kind:, raw:, position:)
97
+ case target_str
98
+ when /\Aurn:/i
99
+ { type: "urn", urn: target_str }
100
+ when /\Ahttps?:\/\//i
101
+ { type: "url", url: target_str }
102
+ when /\A[a-zA-Z][a-zA-Z0-9_-]*:[^:]+/
103
+ split_dataset_qualified(target_str)
104
+ when %r{\A[a-zA-Z0-9_./-]+\z}
105
+ if target_str.include?("/")
106
+ { type: "path", path: target_str }
107
+ else
108
+ { type: "entity_id", id: target_str }
109
+ end
110
+ else
111
+ { type: "entity_id", id: target_str }
112
+ end
113
+ end
114
+ private_class_method :parse_target
115
+
116
+ def split_dataset_qualified(target_str)
117
+ last_colon = target_str.rindex(":")
118
+ dataset = target_str[0...last_colon]
119
+ id = target_str[(last_colon + 1)..]
120
+ { type: "dataset_qualified", dataset: dataset, id: id }
121
+ end
122
+ private_class_method :split_dataset_qualified
123
+
124
+ def validate_target!(kind, target, raw:, position:)
125
+ type = target[:type]
126
+
127
+ case kind
128
+ when "concept", "cite"
129
+ unless %w[urn dataset_qualified].include?(type)
130
+ raise InvalidMentionError.new(
131
+ raw: raw,
132
+ position: position,
133
+ reason: "#{kind} target must be DATASET:ID or URN; " \
134
+ "got #{type} #{target_value_for_error(target).inspect}",
135
+ )
136
+ end
137
+ when "fig", "table", "formula"
138
+ unless %w[entity_id urn].include?(type)
139
+ raise InvalidMentionError.new(
140
+ raw: raw,
141
+ position: position,
142
+ reason: "#{kind} target must be plain ID or URN; " \
143
+ "got #{type} #{target_value_for_error(target).inspect}",
144
+ )
145
+ end
146
+ when "bib"
147
+ unless type == "entity_id"
148
+ raise InvalidMentionError.new(
149
+ raw: raw,
150
+ position: position,
151
+ reason: "bib target must be a plain ID (same-dataset); " \
152
+ "got #{type} #{target_value_for_error(target).inspect}",
153
+ )
154
+ end
155
+ when "link"
156
+ unless type == "url"
157
+ raise InvalidMentionError.new(
158
+ raw: raw,
159
+ position: position,
160
+ reason: "link target must be https:// or http:// URL; " \
161
+ "got #{target_value_for_error(target).inspect}",
162
+ )
163
+ end
164
+ when "image"
165
+ unless %w[path url].include?(type)
166
+ raise InvalidMentionError.new(
167
+ raw: raw,
168
+ position: position,
169
+ reason: "image target must be a path or URL; " \
170
+ "got #{type} #{target_value_for_error(target).inspect}",
171
+ )
172
+ end
173
+ end
174
+ end
175
+ private_class_method :validate_target!
176
+
177
+ def target_value_for_error(target)
178
+ target[:urn] || target[:url] || target[:id] ||
179
+ target[:dataset] ? "#{target[:dataset]}:#{target[:id]}" : target[:path]
180
+ end
181
+ private_class_method :target_value_for_error
182
+
183
+ def strip_label(label)
184
+ label.nil? || label.empty? ? nil : label
185
+ end
186
+ private_class_method :strip_label
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ module Mentions
5
+ # Resolves parsed mention segments against a resolution context.
6
+ # The context provides lookup callables; the resolver dispatches
7
+ # by mention kind + target type.
8
+ module Resolver
9
+ module_function
10
+
11
+ # @param mention [Hash] a parsed mention from Parser.parse
12
+ # @param context [Hash] resolution context with lookup callables
13
+ # @return [Hash] resolved mention with :status
14
+ def resolve(mention, context)
15
+ return mention if mention[:kind] == "text"
16
+
17
+ case mention[:kind]
18
+ when "concept", "cite"
19
+ resolve_concept(mention, context)
20
+ when "fig", "table", "formula"
21
+ resolve_entity(mention, context)
22
+ when "bib"
23
+ resolve_bib(mention, context)
24
+ when "link"
25
+ { status: "external", kind: "link",
26
+ url: mention[:target][:url], label: mention[:label] }
27
+ when "image"
28
+ resolve_image(mention)
29
+ else
30
+ { status: "unresolved", kind: mention[:kind],
31
+ target: mention[:target], label: mention[:label] }
32
+ end
33
+ end
34
+
35
+ def resolve_all(mentions, context)
36
+ mentions.map { |m| resolve(m, context) }
37
+ end
38
+
39
+ def resolve_concept(mention, context)
40
+ target = mention[:target]
41
+ concept = lookup_concept(target, context)
42
+
43
+ return unresolved(mention) unless concept
44
+
45
+ result = {
46
+ status: "resolved",
47
+ kind: mention[:kind],
48
+ concept: concept,
49
+ label: mention[:label],
50
+ }
51
+
52
+ if mention[:kind] == "cite" && context[:concept]
53
+ result[:source] = find_matching_source(target, context[:concept])
54
+ end
55
+
56
+ result
57
+ end
58
+ private_class_method :resolve_concept
59
+
60
+ def resolve_entity(mention, context)
61
+ target = mention[:target]
62
+ entity = nil
63
+
64
+ case target[:type]
65
+ when "entity_id"
66
+ resolver = context[:resolve_entity]
67
+ entity = resolver&.call(mention[:kind], target[:id]) if resolver
68
+ when "urn"
69
+ entity = nil
70
+ end
71
+
72
+ if entity
73
+ { status: "resolved", kind: mention[:kind],
74
+ entity: entity, label: mention[:label] }
75
+ else
76
+ unresolved(mention)
77
+ end
78
+ end
79
+ private_class_method :resolve_entity
80
+
81
+ def resolve_bib(mention, context)
82
+ target = mention[:target]
83
+ entry = context[:resolve_bib_entry]&.call(target[:id])
84
+
85
+ if entry
86
+ { status: "resolved", kind: "bib",
87
+ entry: entry, label: mention[:label] }
88
+ else
89
+ unresolved(mention)
90
+ end
91
+ end
92
+ private_class_method :resolve_bib
93
+
94
+ def resolve_image(mention)
95
+ target = mention[:target]
96
+ if target[:type] == "url"
97
+ { status: "external_image", kind: "image",
98
+ url: target[:url], label: mention[:label] }
99
+ else
100
+ { status: "local_image", kind: "image",
101
+ path: target[:path], label: mention[:label] }
102
+ end
103
+ end
104
+ private_class_method :resolve_image
105
+
106
+ def lookup_concept(target, context)
107
+ resolver = context[:resolve_concept]
108
+ return nil unless resolver
109
+
110
+ case target[:type]
111
+ when "dataset_qualified"
112
+ resolver.call(dataset: target[:dataset], id: target[:id])
113
+ when "urn"
114
+ resolver.call(urn: target[:urn])
115
+ end
116
+ end
117
+ private_class_method :lookup_concept
118
+
119
+ def find_matching_source(target, concept)
120
+ sources = concept.respond_to?(:data) ? concept.data&.sources : concept.sources
121
+ return nil unless sources
122
+
123
+ case target[:type]
124
+ when "dataset_qualified"
125
+ sources.find do |s|
126
+ next unless s.respond_to?(:origin) && s.origin.respond_to?(:ref)
127
+ ref = s.origin.ref
128
+ ref && ref.source == target[:dataset] && ref.id == target[:id]
129
+ end
130
+ when "urn"
131
+ nil
132
+ end
133
+ end
134
+ private_class_method :find_matching_source
135
+
136
+ def unresolved(mention)
137
+ { status: "unresolved", kind: mention[:kind],
138
+ target: mention[:target], label: mention[:label] }
139
+ end
140
+ private_class_method :unresolved
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glossarist
4
+ # Inline mentions — fully declarative {{kind:target}} syntax per
5
+ # concept-model docs/design/inline-mentions.md.
6
+ #
7
+ # Every mention target is a CANONICAL IDENTIFIER. No magic
8
+ # combinations, no bare text, no procedural indirection. The parser
9
+ # REJECTS any non-declarative form with a clear error.
10
+ module Mentions
11
+ autoload :InvalidMentionError, "glossarist/mentions/invalid_mention_error"
12
+ autoload :Parser, "glossarist/mentions/parser"
13
+ autoload :Resolver, "glossarist/mentions/resolver"
14
+ end
15
+ end
@@ -89,8 +89,10 @@ module Glossarist
89
89
  # Consumers inject the resolver at query time.
90
90
 
91
91
  # True if the comprehensive resolves (via the supplied resolver)
92
- # to a concept with status: external.
92
+ # to a concept with status: external. Also true when the
93
+ # comprehensive ConceptRef itself has external: true (inline form).
93
94
  def external_comprehensive?(resolver = nil)
95
+ return true if comprehensive.is_a?(ConceptRef) && comprehensive.external?
94
96
  return false unless comprehensive.is_a?(ConceptRef)
95
97
  return false unless resolver
96
98
 
@@ -98,8 +100,8 @@ module Glossarist
98
100
  concept_is_external?(concept)
99
101
  end
100
102
 
101
- # Array of members whose refs resolve to status: external.
102
- # Empty when no resolver is supplied (detection is opt-in).
103
+ # Array of members whose refs are marked external: true (inline
104
+ # form) OR resolve (via the supplied resolver) to status: external.
103
105
  def external_members(resolver = nil)
104
106
  return [] unless resolver
105
107
 
@@ -125,6 +127,33 @@ module Glossarist
125
127
  candidates.any? { |ref| !has_provided_by?(ref, resolver) }
126
128
  end
127
129
 
130
+ # ── Ellipsis / external inline helpers (no resolver needed) ──
131
+
132
+ # True if any member has a ConceptRef with external: true.
133
+ def has_external_members?
134
+ members.any? { |m| m.is_a?(HyperedgeMember) && m.ref&.external? }
135
+ end
136
+
137
+ # True if the comprehensive ConceptRef has external: true.
138
+ def has_external_comprehensive?
139
+ comprehensive.is_a?(ConceptRef) && comprehensive.external?
140
+ end
141
+
142
+ # True if any member has a ConceptRef with ellipsis: true.
143
+ def has_ellipsis_member?
144
+ members.any? { |m| m.is_a?(HyperedgeMember) && m.ref&.ellipsis? }
145
+ end
146
+
147
+ # Array of members whose refs are marked external: true.
148
+ def inline_external_members
149
+ members.select { |m| m.is_a?(HyperedgeMember) && m.ref&.external? }
150
+ end
151
+
152
+ # Array of members whose refs are marked ellipsis: true.
153
+ def ellipsis_members
154
+ members.select { |m| m.is_a?(HyperedgeMember) && m.ref&.ellipsis? }
155
+ end
156
+
128
157
  # Per-file identity derived from comprehensive + criterion.
129
158
  # Format: `<comprehensive-id-dir>/<criterion-slug>` where the
130
159
  # dir is `<source>-<id>` (downcased, kebab-case) and the slug
@@ -68,7 +68,7 @@ module Glossarist
68
68
  hash = hyperedge.to_hash
69
69
  hash["$id"] = hyperedge.file_id || hyperedge.derived_file_id
70
70
  hash["type"] = hyperedge.class::TYPE_TAG
71
- # Re-order for stable output.
71
+ strip_falsey_concept_ref_flags!(hash)
72
72
  ordered = {}
73
73
  ordered["$id"] = hash.delete("$id")
74
74
  ordered["type"] = hash.delete("type")
@@ -79,11 +79,28 @@ module Glossarist
79
79
  ordered["criterion"] = hash.delete("criterion") if hash.key?("criterion")
80
80
  ordered["sources"] = hash.delete("sources") if hash.key?("sources")
81
81
  ordered["notes"] = hash.delete("notes") if hash.key?("notes")
82
- ordered.merge!(hash) # any future fields append in declaration order
82
+ ordered.merge!(hash)
83
+ strip_falsey_concept_ref_flags!(ordered)
83
84
  ordered.compact!
84
85
 
85
86
  YAML.dump(ordered).gsub(/^---\s*\n/, "---\n")
86
87
  end
88
+
89
+ # Recursively strip `external: false` and `ellipsis: false` from
90
+ # ConceptRef hashes inside the hyperedge's wire output. Keeps
91
+ # YAML clean — these flags only appear when true.
92
+ def strip_falsey_concept_ref_flags!(hash)
93
+ hash.each_value do |value|
94
+ case value
95
+ when Hash
96
+ value.delete("external") if value["external"] == false
97
+ value.delete("ellipsis") if value["ellipsis"] == false
98
+ strip_falsey_concept_ref_flags!(value)
99
+ when Array
100
+ value.each { |item| strip_falsey_concept_ref_flags!(item) if item.is_a?(Hash) }
101
+ end
102
+ end
103
+ end
87
104
  end
88
105
  end
89
106
  end
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- VERSION = "2.13.10"
7
+ VERSION = "2.13.12"
8
8
  end
data/lib/glossarist.rb CHANGED
@@ -65,8 +65,7 @@ module Glossarist
65
65
  autoload :ManagedConcept, "glossarist/managed_concept"
66
66
  autoload :ManagedConceptCollection, "glossarist/managed_concept_collection"
67
67
  autoload :ManagedConceptData, "glossarist/managed_concept_data"
68
- autoload :MentionParser, "glossarist/mention_parser"
69
- autoload :MentionKinds, "glossarist/mention_parser"
68
+ autoload :Mentions, "glossarist/mentions"
70
69
  autoload :NonVerbRep, "glossarist/non_verb_rep"
71
70
  autoload :Pronunciation, "glossarist/pronunciation"
72
71
  autoload :RelatedConcept, "glossarist/related_concept"
@@ -109,12 +108,11 @@ module Glossarist
109
108
  end
110
109
  end
111
110
 
112
- # Parse a single {{...}} mention into a canonical Hash. Defined here
113
- # (rather than via autoload) so calling Glossarist.parse_mention(str)
114
- # triggers the MentionParser autoload chain transparently — method
115
- # calls do not fire autoloads on their own.
116
- def self.parse_mention(text)
117
- MentionParser.parse_mention(text)
111
+ # Parse all {{kind:target}} mentions in text. Returns an array of
112
+ # segments (text + mention objects) per concept-model inline-mentions spec.
113
+ # @see Glossarist::Mentions::Parser.parse
114
+ def self.parse_mentions(text)
115
+ Mentions::Parser.parse(text)
118
116
  end
119
117
 
120
118
  SCHEMA_VERSION = "3"
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.13.10
4
+ version: 2.13.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-02 00:00:00.000000000 Z
11
+ date: 2026-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -293,6 +293,10 @@ files:
293
293
  - lib/glossarist/managed_concept_collection.rb
294
294
  - lib/glossarist/managed_concept_data.rb
295
295
  - lib/glossarist/mention_parser.rb
296
+ - lib/glossarist/mentions.rb
297
+ - lib/glossarist/mentions/invalid_mention_error.rb
298
+ - lib/glossarist/mentions/parser.rb
299
+ - lib/glossarist/mentions/resolver.rb
296
300
  - lib/glossarist/non_concept_entity.rb
297
301
  - lib/glossarist/non_verb_rep.rb
298
302
  - lib/glossarist/non_verbal_reference.rb